
--- LICENSE ---

                                 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.


--- README.md ---

# Minimal SDL binding for Mojo 🔥
Very basic native Mojo bindings to SDL2. No Python is used at runtime.
A Python script to generate different colormaps for the demo is included.

## Usage
Requires SDL installed:
#### Ubuntu
```bash
apt install libsdl2-dev
```
#### Fedora
```bash
dnf install sdl2-devel
```
#### Mac OS
```bash
brew install sdl2
```


Copy `SDL.mojo` to your project or try the Mandelbrot demo ported from [Mojo examples](https://github.com/modularml/mojo/tree/main/examples/mandelbrot.mojo)

## Demo
Run
```bash
mojo mandelbrot.mojo
```
and zoom in/out with the mouse wheel.


--- SDL.mojo ---

from sys import ffi, info

fn get_sdl_lib_path() -> StringLiteral:
    if (info.os_is_linux()):
        var lib_path = '/usr/lib/x86_64-linux-gnu/libSDL2.so'
        try:
            with open('/etc/os-release', 'r') as f:
                var release = f.read()
                if (release.find('Ubuntu') < 0):
                    lib_path = '/usr/lib64/libSDL2.so'
        except:
            print("Can't detect Linux version")
        return lib_path
    if (info.os_is_macos()):
        return '/opt/homebrew/lib/libSDL2.dylib'
    return ""

#    SDL_PIXELFORMAT_RGBA8888 =
#      SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_RGBA,
#                             SDL_PACKEDLAYOUT_8888, 32, 4),

alias SDL_PIXELTYPE_PACKED32 = 6
alias SDL_PACKEDORDER_RGBA = 4
alias SDL_PACKEDLAYOUT_8888 = 6


fn SDL_DEFINE_PIXELFORMAT(type: Int, order: Int, layout: Int, bits: Int, bytes: Int) -> Int:
    return ((1 << 28) | ((type) << 24) | ((order) << 20) | ((layout) << 16) | ((bits) << 8) | ((bytes) << 0))

alias SDL_PIXELFORMAT_RGBA8888 = SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32,
                                                        SDL_PACKEDORDER_RGBA,
                                                        SDL_PACKEDLAYOUT_8888,
                                                        32,
                                                        4)

alias SDL_TEXTUREACCESS_TARGET = 2




@register_passable('trivial')
struct SDL_Window:
    pass

@register_passable('trivial')
struct SDL_Rect:
    var x: Int32
    var y: Int32
    var w: Int32
    var h: Int32

@register_passable('trivial')
struct SDL_PixelFormat:
    pass

@register_passable('trivial')
struct SDL_Renderer:
    pass

@register_passable('trivial')
struct SDL_Texture:
    pass

@register_passable('trivial')
struct SDL_Surface:
    var flags: UInt32
    var format: Pointer[SDL_PixelFormat]
    var w: Int32
    var h: Int32
    var pitch: Int32
    var pixels: Pointer[UInt32]
    var userdata: Pointer[Int8]
    var locked: Int32
    var list_blitmap: Pointer[Int8]
    var clip_rect: SDL_Rect
    var map: Pointer[Int8]
    var refcount: Int32



alias SDL_QUIT = 0x100

alias SDL_KEYDOWN = 0x300
alias SDL_KEYUP   = 0x301
#alias SDL_

alias SDL_MOUSEMOTION     = 0x400
alias SDL_MOUSEBUTTONDOWN = 0x401
alias SDL_MOUSEBUTTONUP   = 0x402
alias SDL_MOUSEWHEEL      = 0x403

@register_passable('trivial')
struct Keysym:
    var scancode: Int32
    var keycode: Int32
    var mod: UInt16
    var unused: UInt32

@register_passable('trivial')
struct MouseMotionEvent:
    var type: UInt32
    var timestamp: UInt32
    var windowID: UInt32
    var which: UInt32
    var state: UInt32
    var x: Int32
    var y: Int32
    var xrel: Int32
    var yrel: Int32

@register_passable('trivial')
struct MouseButtonEvent:
    var type: UInt32
    var timestamp: UInt32
    var windowID: UInt32
    var which: UInt32
    var button: UInt8
    var state: UInt8
    var clicks: UInt8
    var padding1: UInt8
    var x: Int32
    var y: Int32

@register_passable('trivial')
struct MouseWheelEvent:
    var type: UInt32
    var timestamp: UInt32
    var windowID: UInt32
    var which: UInt32
    var x: Int32
    var y: Int32
    var direction: UInt32
    var preciseX: Float32
    var preciseY: Float32
    var mouseX: Int32
    var mouseY: Int32

@register_passable('trivial')
struct Event:
    var type: Int32
    var _padding: SIMD[DType.uint8, 16]
    var _padding2: Int64
    var _padding3: Int64
    def __init__() -> Event:
        return Event { type: 0, _padding: 0, _padding2: 0, _padding3: 0 }

    #fn __init__(inout self):
    #    self.type = 0
    #    self._padding = 0
    #    self._padding2 = 0
    #    self._padding3 = 0
    #    self._padding4 = 0

    def as_keyboard(self) -> Keyevent:
        return Pointer.address_of(self).bitcast[Keyevent]().load()

    def as_mousemotion(self) -> MouseMotionEvent:
        return Pointer.address_of(self).bitcast[MouseMotionEvent]().load()

    def as_mousebutton(self) -> MouseButtonEvent:
        return Pointer.address_of(self).bitcast[MouseButtonEvent]().load()

    def as_mousewheel(self) -> MouseWheelEvent:
        return Pointer.address_of(self).bitcast[MouseWheelEvent]().load()


#alias Event = Keyevent

@register_passable('trivial')
struct Keyevent:
    var type: UInt32
    var timestamp: UInt32
    var windowID: UInt32
    var state: UInt8
    var repeat: UInt8
    var padding2: UInt8
    var padding3: UInt8
    var keysym: Keysym
    
    def __init__(inout self) -> Self:
        #self.value = 0
        self.timestamp = 0
        self.windowID = 0
        self.state = 0
        self.repeat = 0
        self.padding2 = 0
        self.padding3 = 0


# SDL.h
alias c_SDL_Init = fn(w: Int32) -> Int32
alias c_SDL_Quit = fn() -> None

# SDL_video.h
alias c_SDL_CreateWindow = fn(DTypePointer[DType.int8], Int32, Int32, Int32, Int32, Int32) -> Pointer[SDL_Window]
alias c_SDL_DestroyWindow = fn(Pointer[SDL_Window]) -> None
alias c_SDL_GetWindowSurface = fn(s: Pointer[Int8]) -> Pointer[SDL_Surface]
alias c_SDL_UpdateWindowSurface = fn(s: Pointer[Int8]) -> Int32

# SDL_pixels.h
alias c_SDL_MapRGB = fn(Int32, Int32, Int32, Int32) -> UInt32

# SDL_timer.h
alias c_SDL_Delay = fn(Int32) -> UInt32

# SDL_event.h
alias c_SDL_PollEvent = fn(Pointer[Event]) -> Int32

# SDL_render.h
alias c_SDL_CreateRenderer = fn(Pointer[SDL_Window], Int32, UInt32) -> Pointer[SDL_Renderer]
alias c_SDL_CreateWindowAndRenderer = fn(Int32, Int32, UInt32, Pointer[Pointer[Int8]], Pointer[Pointer[SDL_Renderer]]) -> Int32
alias c_SDL_RenderDrawPoint = fn(Pointer[SDL_Renderer], Int32, Int32) -> Int32
alias c_SDL_RenderDrawRect = fn(r: Pointer[SDL_Renderer], rect: Pointer[SDL_Rect]) -> Int32
alias c_SDL_RenderPresent = fn(s: Pointer[SDL_Renderer]) -> Int32
alias c_SDL_RenderClear = fn(s: Pointer[SDL_Renderer]) -> Int32
alias c_SDL_SetRenderDrawColor = fn(Pointer[SDL_Renderer], UInt8, UInt8, UInt8, UInt8) -> Int32
alias SDL_BlendMode = Int
alias c_SDL_SetRenderDrawBlendMode = fn(Pointer[SDL_Renderer], SDL_BlendMode) -> Int32
alias c_SDL_SetRenderTarget = fn(r: Pointer[SDL_Renderer],
#                                 t: Pointer[SDL_Texture]) -> Int32
                                 t: Int64) -> Int32

alias c_SDL_RenderCopy = fn(r: Pointer[SDL_Renderer],
                            t: Int64,  #t: Pointer[SDL_Texture],
                            s: Int64, d: Int64) -> Int32
                            #s: Pointer[SDL_Rect], d: Pointer[SDL_Rect]) -> Int32

# SDL_surface.h
alias c_SDL_FillRect = fn(Pointer[SDL_Surface], Int64, UInt32) -> Int32


# texture
alias c_SDL_CreateTexture = fn(Pointer[SDL_Renderer],
                               UInt32, Int32,
                               Int32, Int32) -> Int64 #Pointer[SDL_Texture]



alias SDL_WINDOWPOS_UNDEFINED = 0x1FFF0000

alias SDL_WINDOW_SHOWN = 0x00000004


struct SDL:
    var Init: c_SDL_Init
    var Quit: c_SDL_Quit

    var CreateWindow: c_SDL_CreateWindow
    var DestroyWindow: c_SDL_DestroyWindow

    var GetWindowSurface: c_SDL_GetWindowSurface
    var UpdateWindowSurface: c_SDL_UpdateWindowSurface
    var CreateRenderer: c_SDL_CreateRenderer
    var CreateWindowAndRenderer: c_SDL_CreateWindowAndRenderer
    var RenderDrawPoint: c_SDL_RenderDrawPoint
    var RenderDrawRect: c_SDL_RenderDrawRect
    var SetRenderDrawColor: c_SDL_SetRenderDrawColor
    var RenderPresent: c_SDL_RenderPresent
    var RenderClear: c_SDL_RenderClear
    var CreateTexture: c_SDL_CreateTexture
    var SetRenderDrawBlendMode: c_SDL_SetRenderDrawBlendMode
    var SetRenderTarget: c_SDL_SetRenderTarget
    var RenderCopy: c_SDL_RenderCopy

    var MapRGB: c_SDL_MapRGB
    var FillRect: c_SDL_FillRect
    var Delay: c_SDL_Delay
    var PollEvent: c_SDL_PollEvent

    fn __init__(inout self):
        print("binding SDL")
        var lib_path = get_sdl_lib_path()
        #let SDL = ffi.DLHandle('/usr/lib64/libSDL2.so')
        var SDL = ffi.DLHandle(lib_path)

        self.Init = SDL.get_function[c_SDL_Init]('SDL_Init')
        self.Quit = SDL.get_function[c_SDL_Quit]('SDL_Quit')

        self.CreateWindow = SDL.get_function[c_SDL_CreateWindow]('SDL_CreateWindow')
        self.DestroyWindow = SDL.get_function[c_SDL_DestroyWindow]('SDL_DestroyWindow')

        self.GetWindowSurface = SDL.get_function[c_SDL_GetWindowSurface]('SDL_GetWindowSurface')
        self.UpdateWindowSurface = SDL.get_function[c_SDL_UpdateWindowSurface]('SDL_UpdateWindowSurface')

        self.CreateRenderer = SDL.get_function[c_SDL_CreateRenderer]('SDL_CreateRenderer')
        self.CreateWindowAndRenderer = SDL.get_function[c_SDL_CreateWindowAndRenderer]('SDL_CreateWindowAndRenderer')
        self.RenderDrawPoint = SDL.get_function[c_SDL_RenderDrawPoint]('SDL_RenderDrawPoint')
        self.RenderDrawRect = SDL.get_function[c_SDL_RenderDrawRect]('SDL_RenderDrawRect')
        self.SetRenderDrawColor = SDL.get_function[c_SDL_SetRenderDrawColor]('SDL_SetRenderDrawColor')
        self.RenderPresent = SDL.get_function[c_SDL_RenderPresent]('SDL_RenderPresent')
        self.RenderClear = SDL.get_function[c_SDL_RenderClear]('SDL_RenderClear')
        self.SetRenderDrawBlendMode = SDL.get_function[c_SDL_SetRenderDrawBlendMode]('SDL_SetRenderDrawBlendMode')
        self.SetRenderTarget = SDL.get_function[c_SDL_SetRenderTarget]('SDL_SetRenderTarget')
        self.RenderCopy = SDL.get_function[c_SDL_RenderCopy]('SDL_RenderCopy')

        self.CreateTexture = SDL.get_function[c_SDL_CreateTexture]('SDL_CreateTexture')


        self.MapRGB = SDL.get_function[c_SDL_MapRGB]('SDL_MapRGB')
        self.FillRect = SDL.get_function[c_SDL_FillRect]('SDL_FillRect')
        self.Delay = SDL.get_function[c_SDL_Delay]('SDL_Delay')
        self.PollEvent = SDL.get_function[c_SDL_PollEvent]('SDL_PollEvent')


--- cmap.mojo ---

alias vec3 = StaticTuple[3]
alias cmap = StaticTuple[256] (
vec3(0, 0, 0),
vec3(2, 0, 0),
vec3(4, 0, 0),
vec3(6, 0, 0),
vec3(8, 0, 0),
vec3(10, 0, 0),
vec3(12, 0, 0),
vec3(14, 0, 0),
vec3(16, 0, 0),
vec3(18, 0, 0),
vec3(20, 0, 0),
vec3(22, 0, 0),
vec3(24, 0, 0),
vec3(26, 0, 0),
vec3(28, 0, 0),
vec3(30, 0, 0),
vec3(32, 0, 0),
vec3(34, 0, 0),
vec3(36, 0, 0),
vec3(38, 0, 0),
vec3(40, 0, 0),
vec3(42, 0, 0),
vec3(44, 0, 0),
vec3(46, 0, 0),
vec3(48, 0, 0),
vec3(50, 0, 0),
vec3(52, 0, 0),
vec3(54, 0, 0),
vec3(56, 0, 0),
vec3(58, 0, 0),
vec3(60, 0, 0),
vec3(62, 0, 0),
vec3(64, 0, 0),
vec3(65, 0, 0),
vec3(68, 0, 0),
vec3(70, 0, 0),
vec3(72, 0, 0),
vec3(73, 0, 0),
vec3(76, 0, 0),
vec3(78, 0, 0),
vec3(80, 0, 0),
vec3(81, 0, 0),
vec3(84, 0, 0),
vec3(86, 0, 0),
vec3(88, 0, 0),
vec3(89, 0, 0),
vec3(92, 0, 0),
vec3(94, 0, 0),
vec3(96, 0, 0),
vec3(97, 0, 0),
vec3(100, 0, 0),
vec3(102, 0, 0),
vec3(104, 0, 0),
vec3(105, 0, 0),
vec3(108, 0, 0),
vec3(110, 0, 0),
vec3(112, 0, 0),
vec3(113, 0, 0),
vec3(116, 0, 0),
vec3(118, 0, 0),
vec3(120, 0, 0),
vec3(121, 0, 0),
vec3(124, 0, 0),
vec3(126, 0, 0),
vec3(128, 0, 0),
vec3(130, 2, 0),
vec3(131, 4, 0),
vec3(134, 6, 0),
vec3(136, 8, 0),
vec3(138, 10, 0),
vec3(140, 12, 0),
vec3(142, 14, 0),
vec3(144, 16, 0),
vec3(146, 18, 0),
vec3(147, 20, 0),
vec3(150, 22, 0),
vec3(152, 24, 0),
vec3(154, 26, 0),
vec3(156, 28, 0),
vec3(158, 30, 0),
vec3(160, 32, 0),
vec3(162, 34, 0),
vec3(163, 36, 0),
vec3(166, 38, 0),
vec3(168, 40, 0),
vec3(170, 42, 0),
vec3(172, 44, 0),
vec3(174, 46, 0),
vec3(176, 48, 0),
vec3(178, 50, 0),
vec3(179, 52, 0),
vec3(182, 54, 0),
vec3(184, 56, 0),
vec3(186, 58, 0),
vec3(188, 60, 0),
vec3(190, 62, 0),
vec3(192, 64, 0),
vec3(194, 66, 0),
vec3(195, 68, 0),
vec3(198, 70, 0),
vec3(200, 72, 0),
vec3(202, 74, 0),
vec3(204, 76, 0),
vec3(206, 78, 0),
vec3(208, 80, 0),
vec3(210, 82, 0),
vec3(211, 84, 0),
vec3(214, 86, 0),
vec3(216, 88, 0),
vec3(218, 90, 0),
vec3(220, 92, 0),
vec3(222, 94, 0),
vec3(224, 96, 0),
vec3(226, 98, 0),
vec3(227, 100, 0),
vec3(230, 102, 0),
vec3(232, 104, 0),
vec3(234, 106, 0),
vec3(236, 108, 0),
vec3(238, 110, 0),
vec3(240, 112, 0),
vec3(242, 114, 0),
vec3(243, 116, 0),
vec3(246, 118, 0),
vec3(248, 120, 0),
vec3(250, 122, 0),
vec3(252, 124, 0),
vec3(254, 126, 0),
vec3(255, 128, 0),
vec3(255, 130, 2),
vec3(255, 132, 4),
vec3(255, 134, 6),
vec3(255, 136, 8),
vec3(255, 138, 11),
vec3(255, 140, 13),
vec3(255, 142, 15),
vec3(255, 144, 16),
vec3(255, 146, 18),
vec3(255, 148, 20),
vec3(255, 150, 22),
vec3(255, 152, 25),
vec3(255, 154, 27),
vec3(255, 156, 29),
vec3(255, 158, 31),
vec3(255, 160, 32),
vec3(255, 162, 34),
vec3(255, 164, 36),
vec3(255, 166, 38),
vec3(255, 168, 40),
vec3(255, 170, 43),
vec3(255, 172, 45),
vec3(255, 174, 47),
vec3(255, 176, 48),
vec3(255, 178, 50),
vec3(255, 180, 52),
vec3(255, 182, 54),
vec3(255, 184, 57),
vec3(255, 186, 59),
vec3(255, 188, 61),
vec3(255, 190, 63),
vec3(255, 192, 65),
vec3(255, 194, 66),
vec3(255, 196, 68),
vec3(255, 198, 70),
vec3(255, 200, 72),
vec3(255, 202, 75),
vec3(255, 204, 77),
vec3(255, 206, 79),
vec3(255, 208, 81),
vec3(255, 210, 82),
vec3(255, 212, 84),
vec3(255, 214, 86),
vec3(255, 216, 89),
vec3(255, 218, 91),
vec3(255, 220, 93),
vec3(255, 222, 95),
vec3(255, 224, 97),
vec3(255, 226, 98),
vec3(255, 228, 100),
vec3(255, 230, 102),
vec3(255, 232, 104),
vec3(255, 234, 107),
vec3(255, 236, 109),
vec3(255, 238, 111),
vec3(255, 240, 113),
vec3(255, 242, 114),
vec3(255, 244, 116),
vec3(255, 246, 118),
vec3(255, 248, 121),
vec3(255, 250, 123),
vec3(255, 252, 125),
vec3(255, 254, 127),
vec3(255, 255, 129),
vec3(255, 255, 131),
vec3(255, 255, 132),
vec3(255, 255, 134),
vec3(255, 255, 136),
vec3(255, 255, 139),
vec3(255, 255, 141),
vec3(255, 255, 143),
vec3(255, 255, 145),
vec3(255, 255, 147),
vec3(255, 255, 148),
vec3(255, 255, 150),
vec3(255, 255, 153),
vec3(255, 255, 155),
vec3(255, 255, 157),
vec3(255, 255, 159),
vec3(255, 255, 161),
vec3(255, 255, 163),
vec3(255, 255, 164),
vec3(255, 255, 166),
vec3(255, 255, 168),
vec3(255, 255, 171),
vec3(255, 255, 173),
vec3(255, 255, 175),
vec3(255, 255, 177),
vec3(255, 255, 179),
vec3(255, 255, 180),
vec3(255, 255, 182),
vec3(255, 255, 185),
vec3(255, 255, 187),
vec3(255, 255, 189),
vec3(255, 255, 191),
vec3(255, 255, 193),
vec3(255, 255, 195),
vec3(255, 255, 196),
vec3(255, 255, 198),
vec3(255, 255, 200),
vec3(255, 255, 203),
vec3(255, 255, 205),
vec3(255, 255, 207),
vec3(255, 255, 209),
vec3(255, 255, 211),
vec3(255, 255, 212),
vec3(255, 255, 214),
vec3(255, 255, 217),
vec3(255, 255, 219),
vec3(255, 255, 221),
vec3(255, 255, 223),
vec3(255, 255, 225),
vec3(255, 255, 227),
vec3(255, 255, 228),
vec3(255, 255, 230),
vec3(255, 255, 232),
vec3(255, 255, 235),
vec3(255, 255, 237),
vec3(255, 255, 239),
vec3(255, 255, 241),
vec3(255, 255, 243),
vec3(255, 255, 244),
vec3(255, 255, 246),
vec3(255, 255, 249),
vec3(255, 255, 251),
vec3(255, 255, 253),
vec3(255, 255, 255),
)


--- gencmap.py ---

#!/usr/bin/env python3

import sys
import matplotlib as mpl

cmap_name = 'afmhot'
if len(sys.argv) > 1:
    cmap_name = sys.argv[1]

cmap8bit = mpl.colormaps[cmap_name].resampled(256)

print("alias vec3 = StaticTuple[3]")
print("alias cmap = StaticTuple[256] (")
for i in range(256):
    c = cmap8bit(i)
    print(f'vec3({int(c[0]*255)}, {int(c[1]*255)}, {int(c[2]*255)}),')
print(')')


--- mandelbrot.mojo ---

# ===----------------------------------------------------------------------=== #
# 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.
# ===----------------------------------------------------------------------=== #

import benchmark
from complex import ComplexSIMD, ComplexFloat64
from math import iota, clamp, log, pow
from python import Python
from algorithm import parallelize, vectorize
from tensor import Tensor
from utils.index import Index

from memory.unsafe import bitcast
from SDL import *
from cmap import cmap

alias float_type = DType.float64
alias simd_width = 2 * simdwidthof[float_type]()

alias width = 480
alias height = 480
alias MAX_ITERS = 500

alias MIN_X = -2.0
alias MAX_X = 0.6
alias MIN_Y = -1.5
alias MAX_Y = 1.5

@no_inline
fn mandelbrot_kernel_SIMD[
    simd_width: Int
](c: ComplexSIMD[float_type, simd_width]) -> SIMD[float_type, simd_width]:
    """A vectorized implementation of the inner mandelbrot computation."""
    var cx = c.re
    var cy = c.im
    var x = SIMD[float_type, simd_width](0)
    var y = SIMD[float_type, simd_width](0)
    var y2 = SIMD[float_type, simd_width](0)
    var iters = SIMD[float_type, simd_width](0)

    var t: SIMD[DType.bool, simd_width] = True
    for i in range(MAX_ITERS):
        if not t.reduce_or():
            break
        y2 = y * y
        y = x.fma(y + y, cy)
        t = x.fma(x, y2) <= 4
        x = x.fma(x, cx - y2)
        iters = t.select(iters + 1, iters)
    return iters


fn main() raises:

    var t = Tensor[float_type](height, width)

    var min_x: Float64 = MIN_X
    var max_x: Float64 = MAX_X
    var min_y: Float64 = MIN_Y
    var max_y: Float64 = MAX_Y

    @parameter
    @no_inline
    fn worker(row: Int):
        var scale_x = (max_x - min_x) / width
        var scale_y = (max_y - min_y) / height

        #print(scale_x)
        @parameter
        @no_inline
        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.data().simd_store[simd_width](
                row * width + col, mandelbrot_kernel_SIMD[simd_width](c)
            )

        # Vectorize the call to compute_vector where call gets a chunk of pixels.
        vectorize[compute_vector, simd_width, width](width)

    _ = t  # Make sure tensor isn't destroyed before benchmark is finished

    var sdl = SDL()

    var res = sdl.Init(0x00000020)
    var window = sdl.CreateWindow(StringRef("Mandelbrot").data,
                                  SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
                                  width, height, SDL_WINDOW_SHOWN)

    var renderer = sdl.CreateRenderer(window, -1, 0)

    var display = sdl.CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height)

    _ = sdl.SetRenderTarget(renderer, display)

    fn redraw(sdl: SDL, t: Tensor[float_type]) raises:
        var gamma: Float64 = 0.3
        var cpow_max = pow(Float64(MAX_ITERS),gamma)
        _ = sdl.SetRenderTarget(renderer, display)
        for y in range(t.shape()[1]):
            for x in range(t.shape()[0]):
                var val = t[x,y]
                var cpow = pow(val, gamma)
                var color = cmap[(255.0*cpow/cpow_max).to_int()]
                var r = color[0]
                var g = color[1]
                var b = color[2]
                _ = sdl.SetRenderDrawColor(renderer, r, g, b, 255)
                _ = sdl.RenderDrawPoint(renderer, y, x)

        _ = sdl.SetRenderTarget(renderer, 0)
        _ = sdl.RenderCopy(renderer, display, 0, 0)
        _ = sdl.RenderPresent(renderer)

    var event = Event()


    fn screen_to_world(sx: Int32, sy: Int32, inout wx: Float64, inout wy: Float64):
        var fsx = sx.cast[DType.float64]()
        var fsy = sy.cast[DType.float64]()
        wx = (max_x - min_x) * fsx/Float64(width) + min_x
        wy = (max_y - min_y) * fsy/Float64(height) + min_y

    var running = True
    var dirty = True

    while running:

        while sdl.PollEvent(Pointer[Event].address_of(event)) != 0:
            if (event.type == SDL_MOUSEWHEEL):
                var mwe = event.as_mousewheel()
                #print(mwe.preciseX, mwe.preciseY)
                var scale = (1 + mwe.preciseY.cast[DType.float64]() / 20.0)
                min_x = (min_x * scale)
                max_x = (max_x * scale)
                min_y = (min_y * scale)
                max_y = (max_y * scale)
                dirty = True
            if (event.type == SDL_QUIT):
                running = False
                break

        if dirty:
            parallelize[worker](height, height)

            dirty = False
        redraw(sdl, t)

        _= sdl.Delay((1000 / 120).to_int())

    _ = t
    sdl.DestroyWindow(window)
    sdl.Quit()



--- .github/workflows/mojopkgscript.yaml ---

name: Build and Release

# define your mojo package name here
# set the path to the directory containing the module files

env:
  PACKAGE_NAME: mojopackage.mojopkg
  MOJO_DIR: src
  MOJO_HOME: /home/runner/.modular/pkg/packages.modular.com_mojo/bin

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
  workflow_dispatch:
    branches: [ main ]

jobs:
  build:

    runs-on: ubuntu-22.04

    steps:
    - uses: actions/checkout@v2

    - name: Install dependencies
      run: |
        curl https://get.modular.com | MODULAR_AUTH=${{ secrets.MODULAR_API }} sh -
        modular install mojo

    - name: Build
      run: |
        ${{ env.MOJO_HOME }}/mojo package ${{ env.MOJO_DIR }} -o ${{ github.workspace }}/${{ env.PACKAGE_NAME }}

    - name: Upload package
      uses: actions/upload-artifact@v3
      with:
        name: ${{ env.PACKAGE_NAME }}
        path: ${{ github.workspace }}/${{ env.PACKAGE_NAME }}

--- README.md ---


## Packaging 📦 Mojo 🔥 via GitHub Action Script


This Repository shows how to use a simple GitHub Action script for compiling a mojo directory into a package 📦.

You can find the action script here --> [Action Script](./.github/workflows/mojopkgscript.yaml).

To use this action script in your own workflow be sure to set your own GitHub secret for the Mojo API key. How to set a GitHub secret can be found [here](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions). The variable used in the Action script for your API is 'MODULAR_API'. Be sure to use the same name or change the variable name in the script.

* Set the package name in the `PACKAGE_NAME` environment variable.
* Set your path of the directory to be packaged in the `MOJO_DIR` environment variable.

Everytime you push to the repository the action script will compile the mojo directory into a package and upload it as an artifact in the GitHub workflow. From there users can download the package and use it in hteir own code base. You can modify this script with additional actions for release deploys on the GitHub Release page.

The `testpkg.mojo` file is a simple mojo that uses the compiled package to call a function.

You can read more about packaging at the Modular documentation page found [here](https://docs.modular.com/mojo/manual/get-started/packages.html)


--- src/__init__.mojo ---



--- src/mojopkg.mojo ---

fn add(a: Int, b: Int) -> Int:
    return a + b

--- testpkg.mojo ---

# This file can only executed without error after building the mojopackage

from mojopackage.mojopkg import add

fn main():
    let res = add(1, 2)
    print(res)


--- .github/workflows/build.yml ---

name: Build and Release

env:
  PACKAGE_NAME: morrow.mojopkg
  MORROW_SRC: morrow

on: workflow_dispatch

jobs:
  build:
    runs-on: ubuntu-22.04

    steps:
      - name: Check out repository code
        uses: actions/checkout@v2
      - name: Install dependencies
        run: |
          curl https://get.modular.com | MODULAR_AUTH=${{ secrets.MODULAR_AUTH }} sh - 
          modular auth ${{ secrets.MODULAR_AUTH }}
          python3 -m venv ~/max-venv && source ~/max-venv/bin/activate
          modular install max
          MAX_PATH=$(modular config max.path) \
            && python3 -m pip install --find-links $MAX_PATH/wheels max-engine
          MAX_PATH=$(modular config max.path) \
            && BASHRC=$( [ -f "$HOME/.bash_profile" ] && echo "$HOME/.bash_profile" || echo "$HOME/.bashrc" ) \
            && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> "$BASHRC" \
            && echo 'export PATH="'$MAX_PATH'/bin:$PATH"' >> "$BASHRC" \
            && source "$BASHRC"
      - name: Build
        run: |
          MAX_PATH=$(modular config max.path) \
            && BASHRC=$( [ -f "$HOME/.bash_profile" ] && echo "$HOME/.bash_profile" || echo "$HOME/.bashrc" ) \
            && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> "$BASHRC" \
            && echo 'export PATH="'$MAX_PATH'/bin:$PATH"' >> "$BASHRC" \
            && source "$BASHRC"
          mojo package ${{ env.MORROW_SRC }} -o ${{ github.workspace }}/${{ env.PACKAGE_NAME }}
      - name: Upload package
        uses: actions/upload-artifact@v3
        with:
          name: ${{ env.PACKAGE_NAME }}
          path: ${{ github.workspace }}/${{ env.PACKAGE_NAME }}


--- .github/workflows/test.yml ---

name: Run Tests

on: [pull_request, push]

env:
  PACKAGE_NAME: morrow.mojopkg
  MORROW_SRC: morrow

jobs:
  test:
    runs-on: ubuntu-22.04

    steps:
      - name: Check out repository code
        uses: actions/checkout@v2
      - name: Install dependencies
        run: |
          curl https://get.modular.com | MODULAR_AUTH=${{ secrets.MODULAR_AUTH }} sh - 
          modular auth ${{ secrets.MODULAR_AUTH }}
          python3 -m venv ~/max-venv && source ~/max-venv/bin/activate
          modular install max
          MAX_PATH=$(modular config max.path) \
            && python3 -m pip install --find-links $MAX_PATH/wheels max-engine
      - name: Test
        run: |
          MAX_PATH=$(modular config max.path) \
            && BASHRC=$( [ -f "$HOME/.bash_profile" ] && echo "$HOME/.bash_profile" || echo "$HOME/.bashrc" ) \
            && echo 'export MODULAR_HOME="'$HOME'/.modular"' >> "$BASHRC" \
            && echo 'export PATH="'$MAX_PATH'/bin:$PATH"' >> "$BASHRC" \
            && source "$BASHRC"
          mojo run test.mojo


--- .gitignore ---

.vscode
.idea

--- LICENSE ---

                                 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.


--- Makefile ---

test:
	mojo run test.mojo

format:
	mojo format .

build:
	mojo package morrow -o morrow.mojopkg 

--- README.md ---

# Morrow.mojo: Human-friendly date & time for Mojo 🔥

<p align="center">
  <a href="https://github.com/mojoto/morrow.mojo/actions/workflows/test.yml">
    <img src="https://github.com/mojoto/morrow.mojo/actions/workflows/test.yml/badge.svg" alt="Test" />
  </a>
  <a href="https://github.com/mojoto/morrow.mojo/releases">
    <img alt="GitHub release" src="https://img.shields.io/github/v/release/mojoto/morrow.mojo">
  </a>
</p>

**Morrow** is a Mojo library that provides human-friendly method for managing, formatting, and transforming dates, times, and timestamps.

Morrow is heavily inspired by [arrow](https://github.com/arrow-py/arrow), and thanks for its elegant design.

## Features

- TimeZone-aware and UTC by default.
- Support format and parse strings.
- Support for the [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) standard.

## Preparation

You have three ways to reference this library:

- Download morrow.mojopkg from [releases](https://github.com/mojoto/morrow.mojo/releases).
- Clone this project and execute `make build` to build morrow.mojopkg.
- Directly copy the `morrow` directory of this project to your own project.

## Usage

```python
from morrow import Morrow, TimeZone

# Get local date and time.
var now = Morrow.now()
print(str(now))  # 2023-10-01T20:10:25.188957+08:00

# Get UTC date and time.
var utcnow = Morrow.utcnow()
print(str(utcnow))  # 2023-10-01T20:10:25.954638+00:00

# Get local time from POSIX timestamp.
var t = Morrow.fromtimestamp(1696089600)
print(str(t))  # 2023-10-01T00:00:00.000000+08:00

# Get UTC time from POSIX timestamp.
var utc_t = Morrow.utcfromtimestamp(1696089600)
print(str(utc_t))  # 2023-09-30T16:00:00.000000+00:00

# Get ISO format.
var m = Morrow(2023, 10, 1, 0, 0, 0, 1234)
print(m.isoformat())  # 2023-10-01T00:00:00.001234

# custom format
var m = Morrow(2023, 10, 1, 0, 0, 0, 1234)
print(m.format("YYYY-MM-DD HH:mm:ss.SSSSSS ZZ"))  # 2023-10-01 00:00:00.001234 +00:00
print(m.format("dddd, DD MMM YYYY HH:mm:ss ZZZ"))  # Sunday, 01 Oct 2023 00:00:00 UTC
print(m.format("YYYY[Y]MM[M]DD[D]"))  # 2023Y10M01D

# Get ISO format with time zone.
var m_beijing = Morrow(2023, 10, 1, 0, 0, 0, 1234, TimeZone(28800, 'Bejing'))
print(m_beijing.isoformat(timespec="seconds"))  # 2023-10-01T00:00:00+08:00

# Get time zone offset.
print(TimeZone.from_utc('UTC+08:00').offset)  # 28800

# Subtract two dates.
var timedelta = Morrow(2023, 10, 2, 10, 0, 0) - Morrow(2023, 10, 1, 10, 0, 0)
print(str(timedelta))  # 1 day, 0:00:00

# Return proleptic Gregorian ordinal for the year, month and day.
var m_10_1 = Morrow(2023, 10, 1)
var ordinal = m_10_1.toordinal()
print(ordinal)  # 738794

# Construct a Morrow from a proleptic Gregorian ordinal.
var m_10_1_ = Morrow.fromordinal(ordinal)
print(str(m_10_1_))  # 2023-10-01T00:00:00.000000

# Convert Morrow to python datetime
var py_dt = now.to_py()
print(py_dt.isoformat())  # 2023-10-01T20:10:25.188957

# Convert python datetime to Morrow
var m_from_py = Morrow.from_py(py_dt)
print(m_from_py)  # 2023-10-01T20:10:25.188957

```


--- morrow/__init__.mojo ---

from .morrow import Morrow
from .timezone import TimeZone
from .timedelta import TimeDelta

alias __version__ = "0.4.0"


--- morrow/_libc.mojo ---

from memory.unsafe import Pointer


alias c_void = UInt8
alias c_char = UInt8
alias c_schar = Int8
alias c_uchar = UInt8
alias c_short = Int16
alias c_ushort = UInt16
alias c_int = Int32
alias c_uint = UInt32
alias c_long = Int64
alias c_ulong = UInt64
alias c_float = Float32
alias c_double = Float64


@value
@register_passable("trivial")
struct CTimeval:
    var tv_sec: Int  # Seconds
    var tv_usec: Int  # Microseconds

    fn __init__(tv_sec: Int = 0, tv_usec: Int = 0) -> Self:
        return Self {tv_sec: tv_sec, tv_usec: tv_usec}


@value
@register_passable("trivial")
struct CTm:
    var tm_sec: Int32  # Seconds
    var tm_min: Int32  # Minutes
    var tm_hour: Int32  # Hour
    var tm_mday: Int32  # Day of the month
    var tm_mon: Int32  # Month
    var tm_year: Int32  # Year minus 1900
    var tm_wday: Int32  # Day of the week
    var tm_yday: Int32  # Day of the year
    var tm_isdst: Int32  # Daylight savings flag
    var tm_gmtoff: Int64  # localtime zone offset seconds

    fn __init__() -> Self:
        return Self {
            tm_sec: 0,
            tm_min: 0,
            tm_hour: 0,
            tm_mday: 0,
            tm_mon: 0,
            tm_year: 0,
            tm_wday: 0,
            tm_yday: 0,
            tm_isdst: 0,
            tm_gmtoff: 0,
        }


@always_inline
fn c_gettimeofday() -> CTimeval:
    var tv = CTimeval()
    var p_tv = Pointer[CTimeval].address_of(tv)
    external_call["gettimeofday", NoneType, Pointer[CTimeval], Int32](p_tv, 0)
    return tv


@always_inline
fn c_localtime(owned tv_sec: Int) -> CTm:
    var p_tv_sec = Pointer[Int].address_of(tv_sec)
    var tm = external_call["localtime", Pointer[CTm], Pointer[Int]](p_tv_sec).load()
    return tm


@always_inline
fn c_strptime(time_str: String, time_format: String) -> CTm:
    var tm = CTm()
    var p_tm = Pointer[CTm].address_of(tm)
    external_call["strptime", NoneType, Pointer[c_char], Pointer[c_char], Pointer[CTm]](
        to_char_ptr(time_str), to_char_ptr(time_format), p_tm
    )
    return tm


@always_inline
fn c_gmtime(owned tv_sec: Int) -> CTm:
    var p_tv_sec = Pointer[Int].address_of(tv_sec)
    var tm = external_call["gmtime", Pointer[CTm], Pointer[Int]](p_tv_sec).load()
    return tm


fn to_char_ptr(s: String) -> Pointer[c_char]:
    """Only ASCII-based strings."""
    var ptr = Pointer[c_char]().alloc(len(s))
    for i in range(len(s)):
        ptr.store(i, ord(s[i]))
    return ptr


--- morrow/_py.mojo ---

from python import Python


fn py_dt_datetime() raises -> PythonObject:
    var _datetime = Python.import_module("datetime")
    return _datetime.datetime


fn py_time() raises -> PythonObject:
    var _time = Python.import_module("time")
    return _time


--- morrow/constants.mojo ---

from utils import StaticTuple


# todo: hardcode for tmp
alias _MAX_TIMESTAMP: Int = 32503737600
alias MAX_TIMESTAMP = _MAX_TIMESTAMP
alias MAX_TIMESTAMP_MS = MAX_TIMESTAMP * 1000
alias MAX_TIMESTAMP_US = MAX_TIMESTAMP * 1_000_000

alias _DAYS_IN_MONTH = VariadicList[Int](
    -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
)
alias _DAYS_BEFORE_MONTH = VariadicList[Int](
    -1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
)  # -1 is a placeholder for indexing purposes.


alias MONTH_NAMES = StaticTuple[StringLiteral, 13](
    "",
    "January",
    "February",
    "March",
    "April",
    "May",
    "June",
    "July",
    "August",
    "September",
    "October",
    "November",
    "December",
)

alias MONTH_ABBREVIATIONS = StaticTuple[StringLiteral, 13](
    "",
    "Jan",
    "Feb",
    "Mar",
    "Apr",
    "May",
    "Jun",
    "Jul",
    "Aug",
    "Sep",
    "Oct",
    "Nov",
    "Dec",
)

alias DAY_NAMES = StaticTuple[StringLiteral, 8](
    "",
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday",
    "Sunday",
)
alias DAY_ABBREVIATIONS = StaticTuple[StringLiteral, 8](
    "", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
)


--- morrow/formatter.mojo ---

from collections.vector import InlinedFixedVector
from utils.static_tuple import StaticTuple
from .util import rjust
from .constants import MONTH_NAMES, MONTH_ABBREVIATIONS, DAY_NAMES, DAY_ABBREVIATIONS
from .timezone import UTC_TZ

alias formatter = _Formatter()


struct _Formatter:
    var _sub_chrs: InlinedFixedVector[Int, 128]

    fn __init__(inout self):
        self._sub_chrs = InlinedFixedVector[Int, 128](0)
        for i in range(128):
            self._sub_chrs[i] = 0
        self._sub_chrs[_Y] = 4
        self._sub_chrs[_M] = 4
        self._sub_chrs[_D] = 2
        self._sub_chrs[_d] = 4
        self._sub_chrs[_H] = 2
        self._sub_chrs[_h] = 2
        self._sub_chrs[_m] = 2
        self._sub_chrs[_s] = 2
        self._sub_chrs[_S] = 6
        self._sub_chrs[_Z] = 3
        self._sub_chrs[_A] = 1
        self._sub_chrs[_a] = 1

    fn format(self, m: Morrow, fmt: String) raises -> String:
        """
        "YYYY[abc]MM" -> repalce("YYYY") + "abc" + replace("MM")
        """
        if len(fmt) == 0:
            return ""
        var ret: String = ""
        var in_bracket = False
        var start_idx = 0
        for i in range(len(fmt)):
            if fmt[i] == "[":
                if in_bracket:
                    ret += "["
                else:
                    in_bracket = True
                ret += self.replace(m, fmt[start_idx:i])
                start_idx = i + 1
            elif fmt[i] == "]":
                if in_bracket:
                    ret += fmt[start_idx:i]
                    in_bracket = False
                else:
                    ret += self.replace(m, fmt[start_idx:i])
                    ret += "]"
                start_idx = i + 1
        if in_bracket:
            ret += "["
        if start_idx < len(fmt):
            ret += self.replace(m, fmt[start_idx:])
        return ret

    fn replace(self, m: Morrow, s: String) raises -> String:
        """
        split token and replace
        """
        if len(s) == 0:
            return ""
        var ret: String = ""
        var match_chr_ord = 0
        var match_count = 0
        for i in range(len(s)):
            var c = ord(s[i])
            if 0 < c < 128 and self._sub_chrs[c] > 0:
                if c == match_chr_ord:
                    match_count += 1
                else:
                    ret += self.replace_token(m, match_chr_ord, match_count)
                    match_chr_ord = c
                    match_count = 1
                if match_count == self._sub_chrs[c]:
                    ret += self.replace_token(m, match_chr_ord, match_count)
                    match_chr_ord = 0
            else:
                if match_chr_ord > 0:
                    ret += self.replace_token(m, match_chr_ord, match_count)
                    match_chr_ord = 0
                ret += s[i]
        if match_chr_ord > 0:
            ret += self.replace_token(m, match_chr_ord, match_count)
        return ret

    fn replace_token(self, m: Morrow, token: Int, token_count: Int) raises -> String:
        if token == _Y:
            if token_count == 1:
                return "Y"
            if token_count == 2:
                return rjust(m.year, 4, "0")[2:4]
            if token_count == 4:
                return rjust(m.year, 4, "0")
        elif token == _M:
            if token_count == 1:
                return String(m.month)
            if token_count == 2:
                return rjust(m.month, 2, "0")
            if token_count == 3:
                return String(MONTH_ABBREVIATIONS[m.month])
            if token_count == 4:
                return String(MONTH_NAMES[m.month])
        elif token == _D:
            if token_count == 1:
                return String(m.day)
            if token_count == 2:
                return rjust(m.day, 2, "0")
        elif token == _H:
            if token_count == 1:
                return String(m.hour)
            if token_count == 2:
                return rjust(m.hour, 2, "0")
        elif token == _h:
            var h_12 = m.hour
            if m.hour > 12:
                h_12 -= 12
            if token_count == 1:
                return String(h_12)
            if token_count == 2:
                return rjust(h_12, 2, "0")
        elif token == _m:
            if token_count == 1:
                return String(m.minute)
            if token_count == 2:
                return rjust(m.minute, 2, "0")
        elif token == _s:
            if token_count == 1:
                return String(m.second)
            if token_count == 2:
                return rjust(m.second, 2, "0")
        elif token == _S:
            if token_count == 1:
                return String(m.microsecond // 100000)
            if token_count == 2:
                return rjust(m.microsecond // 10000, 2, "0")
            if token_count == 3:
                return rjust(m.microsecond // 1000, 3, "0")
            if token_count == 4:
                return rjust(m.microsecond // 100, 4, "0")
            if token_count == 5:
                return rjust(m.microsecond // 10, 5, "0")
            if token_count == 6:
                return rjust(m.microsecond, 6, "0")
        elif token == _d:
            if token_count == 1:
                return String(m.isoweekday())
            if token_count == 3:
                return String(DAY_ABBREVIATIONS[m.isoweekday()])
            if token_count == 4:
                return String(DAY_NAMES[m.isoweekday()])
        elif token == _Z:
            if token_count == 3:
                return UTC_TZ.name if m.tz.is_none() else m.tz.name
            var separator = "" if token_count == 1 else ":"
            if m.tz.is_none():
                return UTC_TZ.format(separator)
            else:
                return m.tz.format(separator)

        elif token == _a:
            return "am" if m.hour < 12 else "pm"
        elif token == _A:
            return "AM" if m.hour < 12 else "PM"
        return ""


alias _Y = ord("Y")
alias _M = ord("M")
alias _D = ord("D")
alias _d = ord("d")
alias _H = ord("H")
alias _h = ord("h")
alias _m = ord("m")
alias _s = ord("s")
alias _S = ord("S")
alias _X = ord("X")
alias _x = ord("x")
alias _Z = ord("Z")
alias _A = ord("A")
alias _a = ord("a")


--- morrow/morrow.mojo ---

from ._py import py_dt_datetime
from .util import normalize_timestamp, rjust, _ymd2ord, _days_before_year
from ._libc import c_gettimeofday, c_localtime, c_gmtime, c_strptime
from ._libc import CTimeval, CTm
from .timezone import TimeZone
from .timedelta import TimeDelta
from .formatter import formatter
from .constants import _DAYS_BEFORE_MONTH, _DAYS_IN_MONTH
from python.object import PythonObject
from python import Python


alias _DI400Y = 146097  # number of days in 400 years
alias _DI100Y = 36524  #    "    "   "   " 100   "
alias _DI4Y = 1461  #    "    "   "   "   4   "


@value
struct Morrow(StringableRaising):
    var year: Int
    var month: Int
    var day: Int
    var hour: Int
    var minute: Int
    var second: Int
    var microsecond: Int
    var tz: TimeZone

    fn __init__(
        inout self,
        year: Int,
        month: Int,
        day: Int,
        hour: Int = 0,
        minute: Int = 0,
        second: Int = 0,
        microsecond: Int = 0,
        tz: TimeZone = TimeZone.none(),
    ) raises:
        self.year = year
        self.month = month
        self.day = day
        self.hour = hour
        self.minute = minute
        self.second = second
        self.microsecond = microsecond
        self.tz = tz

    @staticmethod
    fn now() raises -> Self:
        var t = c_gettimeofday()
        return Self._fromtimestamp(t, False)

    @staticmethod
    fn utcnow() raises -> Self:
        var t = c_gettimeofday()
        return Self._fromtimestamp(t, True)

    @staticmethod
    fn _fromtimestamp(t: CTimeval, utc: Bool) raises -> Self:
        var tm: CTm
        var tz: TimeZone
        if utc:
            tm = c_gmtime(t.tv_sec)
            tz = TimeZone(0, "UTC")
        else:
            tm = c_localtime(t.tv_sec)
            tz = TimeZone(int(tm.tm_gmtoff), "local")

        var result = Self(
            int(tm.tm_year) + 1900,
            int(tm.tm_mon) + 1,
            int(tm.tm_mday),
            int(tm.tm_hour),
            int(tm.tm_min),
            int(tm.tm_sec),
            t.tv_usec,
            tz,
        )
        return result

    @staticmethod
    fn fromtimestamp(timestamp: Float64) raises -> Self:
        var timestamp_ = normalize_timestamp(timestamp)
        var t = CTimeval(int(timestamp_))
        return Self._fromtimestamp(t, False)

    @staticmethod
    fn utcfromtimestamp(timestamp: Float64) raises -> Self:
        var timestamp_ = normalize_timestamp(timestamp)
        var t = CTimeval(int(timestamp_))
        return Self._fromtimestamp(t, True)

    @staticmethod
    fn strptime(
        date_str: String, fmt: String, tzinfo: TimeZone = TimeZone.none()
    ) raises -> Self:
        """
        Create a Morrow instance from a date string and format,
        in the style of ``datetime.strptime``.  Optionally replaces the parsed TimeZone.

        Usage::

        >>> Morrow.strptime('20-01-2019 15:49:10', '%d-%m-%Y %H:%M:%S')
            <Morrow [2019-01-20T15:49:10+00:00]>
        """
        var tm = c_strptime(date_str, fmt)
        var tz = TimeZone(int(tm.tm_gmtoff)) if tzinfo.is_none() else tzinfo
        return Self(
            int(tm.tm_year) + 1900,
            int(tm.tm_mon) + 1,
            int(tm.tm_mday),
            int(tm.tm_hour),
            int(tm.tm_min),
            int(tm.tm_sec),
            0,
            tz,
        )

    @staticmethod
    fn strptime(date_str: String, fmt: String, tz_str: String) raises -> Self:
        """
        Create a Morrow instance by time_zone_string with utc format.

        Usage::

        >>> Morrow.strptime('20-01-2019 15:49:10', '%d-%m-%Y %H:%M:%S', '+08:00')
            <Morrow [2019-01-20T15:49:10+08:00]>
        """
        var tzinfo = TimeZone.from_utc(tz_str)
        return Self.strptime(date_str, fmt, tzinfo)

    fn format(self, fmt: String = "YYYY-MM-DD HH:mm:ss ZZ") raises -> String:
        """Returns a string representation of the `Morrow`
        formatted according to the provided format string.

        :param fmt: the format string.

        Usage::
            >>> var m = Morrow.now()
            >>> m.format('YYYY-MM-DD HH:mm:ss ZZ')
            '2013-05-09 03:56:47 -00:00'

            >>> m.format('MMMM DD, YYYY')
            'May 09, 2013'

            >>> m.format()
            '2013-05-09 03:56:47 -00:00'

        """
        return formatter.format(self, fmt)

    fn isoformat(
        self, sep: String = "T", timespec: StringLiteral = "auto"
    ) raises -> String:
        """Return the time formatted according to ISO.

        The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.

        If self.tzinfo is not None, the UTC offset is also attached, giving
        giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.

        Optional argument sep specifies the separator between date and
        time, default 'T'.

        The optional argument timespec specifies the number of additional
        terms of the time to include. Valid options are 'auto', 'hours',
        'minutes', 'seconds', 'milliseconds' and 'microseconds'.
        """
        var date_str = (
            rjust(self.year, 4, "0")
            + "-"
            + rjust(self.month, 2, "0")
            + "-"
            + rjust(self.day, 2, "0")
        )
        var time_str = String("")
        if timespec == "auto" or timespec == "microseconds":
            time_str = (
                rjust(self.hour, 2, "0")
                + ":"
                + rjust(self.minute, 2, "0")
                + ":"
                + rjust(self.second, 2, "0")
                + "."
                + rjust(self.microsecond, 6, "0")
            )
        elif timespec == "milliseconds":
            time_str = (
                rjust(self.hour, 2, "0")
                + ":"
                + rjust(self.minute, 2, "0")
                + ":"
                + rjust(self.second, 2, "0")
                + "."
                + rjust(self.microsecond // 1000, 3, "0")
            )
        elif timespec == "seconds":
            time_str = (
                rjust(self.hour, 2, "0")
                + ":"
                + rjust(self.minute, 2, "0")
                + ":"
                + rjust(self.second, 2, "0")
            )
        elif timespec == "minutes":
            time_str = rjust(self.hour, 2, "0") + ":" + rjust(self.minute, 2, "0")
        elif timespec == "hours":
            time_str = rjust(self.hour, 2, "0")
        else:
            raise Error()
        if self.tz.is_none():
            return sep.join(date_str, time_str)
        else:
            return sep.join(date_str, time_str) + self.tz.format()

    fn toordinal(self) raises -> Int:
        """Return proleptic Gregorian ordinal for the year, month and day.

        January 1 of year 1 is day 1.  Only the year, month and day values
        contribute to the result.
        """
        return _ymd2ord(self.year, self.month, self.day)

    @staticmethod
    fn fromordinal(ordinal: Int) raises -> Self:
        """Construct a Morrow from a proleptic Gregorian ordinal.

        January 1 of year 1 is day 1.  Only the year, month and day are
        non-zero in the result.
        """
        # n is a 1-based index, starting at 1-Jan-1.  The pattern of leap years
        # repeats exactly every 400 years.  The basic strategy is to find the
        # closest 400-year boundary at or before n, then work with the offset
        # from that boundary to n.  Life is much clearer if we subtract 1 from
        # n first -- then the values of n at 400-year boundaries are exactly
        # those divisible by _DI400Y:
        #
        #     D  M   Y            n              n-1
        #     -- --- ----        ----------     ----------------
        #     31 Dec -400        -_DI400Y       -_DI400Y -1
        #      1 Jan -399         -_DI400Y +1   -_DI400Y      400-year boundary
        #     ...
        #     30 Dec  000        -1             -2
        #     31 Dec  000         0             -1
        #      1 Jan  001         1              0            400-year boundary
        #      2 Jan  001         2              1
        #      3 Jan  001         3              2
        #     ...
        #     31 Dec  400         _DI400Y        _DI400Y -1
        #      1 Jan  401         _DI400Y +1     _DI400Y      400-year boundary
        var n = ordinal
        n -= 1
        var n400 = n // _DI400Y
        n = n % _DI400Y
        var year = n400 * 400 + 1  # ..., -399, 1, 401, ...

        # Now n is the (non-negative) offset, in days, from January 1 of year, to
        # the desired date.  Now compute how many 100-year cycles precede n.
        # Note that it's possible for n100 to equal 4!  In that case 4 full
        # 100-year cycles precede the desired day, which implies the desired
        # day is December 31 at the end of a 400-year cycle.
        var n100 = n // _DI100Y
        n = n % _DI100Y

        # Now compute how many 4-year cycles precede it.
        var n4 = n // _DI4Y
        n = n % _DI4Y

        # And now how many single years.  Again n1 can be 4, and again meaning
        # that the desired day is December 31 at the end of the 4-year cycle.
        var n1 = n // 365
        n = n % 365

        year += n100 * 100 + n4 * 4 + n1
        if n1 == 4 or n100 == 4:
            return Self(year - 1, 12, 31)

        # Now the year is correct, and n is the offset from January 1.  We find
        # the month via an estimate that's either exact or one too large.
        var leapyear = n1 == 3 and (n4 != 24 or n100 == 3)
        var month = (n + 50) >> 5
        var preceding: Int
        if month > 2 and leapyear:
            preceding = _DAYS_BEFORE_MONTH[month] + 1
        else:
            preceding = _DAYS_BEFORE_MONTH[month]
        if preceding > n:  # estimate is too large
            month -= 1
            if month == 2 and leapyear:
                preceding -= _DAYS_BEFORE_MONTH[month] + 1
            else:
                preceding -= _DAYS_BEFORE_MONTH[month]
        n -= preceding

        # Now the year and month are correct, and n is the offset from the
        # start of that month:  we're done!
        return Self(year, month, n + 1)

    fn isoweekday(self) raises -> Int:
        # "Return day of the week, where Monday == 1 ... Sunday == 7."
        # 1-Jan-0001 is a Monday
        return self.toordinal() % 7 or 7

    fn __str__(self) raises -> String:
        return self.isoformat()

    fn __sub__(self, other: Self) raises -> TimeDelta:
        var days1 = self.toordinal()
        var days2 = other.toordinal()
        var secs1 = self.second + self.minute * 60 + self.hour * 3600
        var secs2 = other.second + other.minute * 60 + other.hour * 3600
        var base = TimeDelta(
            days1 - days2, secs1 - secs2, self.microsecond - other.microsecond
        )
        return base

    fn to_py(self) raises -> PythonObject:
        # todo: add tz later
        var dateimte = Python.import_module("datetime")
        return dateimte.datetime(
            self.year,
            self.month,
            self.day,
            self.hour,
            self.minute,
            self.second,
            self.microsecond,
        )

    @staticmethod
    fn from_py(py_datetime: PythonObject) raises -> Morrow:
        # Python.is_type not working, use __class__.__name__ instead
        if py_datetime.__class__.__name__ == "datetime":
            return Morrow(
                int(py_datetime.year),
                int(py_datetime.month),
                int(py_datetime.day),
                int(py_datetime.hour),
                int(py_datetime.minute),
                int(py_datetime.second),
                int(py_datetime.second),
            )
        elif py_datetime.__class__.__name__ == "date":
            return Morrow(
                int(py_datetime.year),
                int(py_datetime.month),
                int(py_datetime.day),
            )
        else:
            raise Error(
                "invalid python object, only support py builtin datetime or date"
            )


--- morrow/timedelta.mojo ---

from .util import rjust

alias SECONDS_OF_DAY = 24 * 3600


struct TimeDelta(Stringable):
    var days: Int
    var seconds: Int
    var microseconds: Int

    fn __init__(
        inout self,
        days: Int = 0,
        seconds: Int = 0,
        microseconds: Int = 0,
        milliseconds: Int = 0,
        minutes: Int = 0,
        hours: Int = 0,
        weeks: Int = 0,
    ):
        self.days = 0
        self.seconds = 0
        self.microseconds = 0

        var days_ = days
        var seconds_ = seconds
        var microseconds_ = microseconds

        # Normalize everything to days, seconds, microseconds.
        days_ += weeks * 7
        seconds_ += minutes * 60 + hours * 3600
        microseconds_ += milliseconds * 1000

        self.days = days_
        days_ = seconds_ // SECONDS_OF_DAY
        seconds_ = seconds_ % SECONDS_OF_DAY
        self.days += days_
        self.seconds += seconds_

        seconds_ = microseconds_ // 1000000
        microseconds_ = microseconds_ % 1000000
        days_ = seconds_ // SECONDS_OF_DAY
        seconds_ = seconds_ % SECONDS_OF_DAY
        self.days += days_
        self.seconds += seconds_

        seconds_ = microseconds_ // 1000000
        self.microseconds = microseconds_ % 1000000
        self.seconds += seconds_
        days_ = self.seconds // SECONDS_OF_DAY
        self.seconds = self.seconds % SECONDS_OF_DAY
        self.days += days_

    fn __copyinit__(inout self, other: Self):
        self.days = other.days
        self.seconds = other.seconds
        self.microseconds = other.microseconds

    fn __str__(self) -> String:
        var mm = self.seconds // 60
        var ss = self.seconds % 60
        var hh = mm // 60
        mm = mm % 60
        var s = String(hh) + ":" + rjust(mm, 2, "0") + ":" + rjust(ss, 2, "0")
        if self.days:
            if abs(self.days) != 1:
                s = String(self.days) + " days, " + s
            else:
                s = String(self.days) + " day, " + s
        if self.microseconds:
            s = s + rjust(self.microseconds, 6, "0")
        return s

    fn total_seconds(self) -> Float64:
        """Total seconds in the duration."""
        return (
            (self.days * 86400 + self.seconds) * 10**6 + self.microseconds
        ) / 10**6

    @always_inline
    fn __add__(self, other: Self) -> Self:
        return Self(
            self.days + other.days,
            self.seconds + other.seconds,
            self.microseconds + other.microseconds,
        )

    fn __radd__(self, other: Self) -> Self:
        return self.__add__(other)

    fn __sub__(self, other: Self) -> Self:
        return Self(
            self.days - other.days,
            self.seconds - other.seconds,
            self.microseconds - other.microseconds,
        )

    fn __rsub__(self, other: Self) -> Self:
        return Self(
            other.days - self.days,
            other.seconds - self.seconds,
            other.microseconds - self.microseconds,
        )

    fn __neg__(self) -> Self:
        return Self(-self.days, -self.seconds, -self.microseconds)

    fn __pos__(self) -> Self:
        return self

    def __abs__(self) -> Self:
        if self.days < 0:
            return -self
        else:
            return self

    @always_inline
    fn __mul__(self, other: Int) -> Self:
        return Self(
            self.days * other,
            self.seconds * other,
            self.microseconds * other,
        )

    fn __rmul__(self, other: Int) -> Self:
        return self.__mul__(other)

    fn _to_microseconds(self) -> Int:
        return (self.days * SECONDS_OF_DAY + self.seconds) * 1000000 + self.microseconds

    fn __mod__(self, other: Self) -> Self:
        var r = self._to_microseconds() % other._to_microseconds()
        return Self(0, 0, r)

    fn __eq__(self, other: Self) -> Bool:
        return (
            self.days == other.days
            and self.seconds == other.seconds
            and self.microseconds == other.microseconds
        )

    @always_inline
    fn __le__(self, other: Self) -> Bool:
        if self.days < other.days:
            return True
        elif self.days == other.days:
            if self.seconds < other.seconds:
                return True
            elif (
                self.seconds == other.seconds
                and self.microseconds <= other.microseconds
            ):
                return True
        return False

    @always_inline
    fn __lt__(self, other: Self) -> Bool:
        if self.days < other.days:
            return True
        elif self.days == other.days:
            if self.seconds < other.seconds:
                return True
            elif (
                self.seconds == other.seconds and self.microseconds < other.microseconds
            ):
                return True
        return False

    fn __ge__(self, other: Self) -> Bool:
        return not self.__lt__(other)

    fn __gt__(self, other: Self) -> Bool:
        return not self.__le__(other)

    fn __bool__(self) -> Bool:
        return self.days != 0 or self.seconds != 0 or self.microseconds != 0


alias Min = TimeDelta(-99999999)
alias Max = TimeDelta(days=99999999)
alias Resolution = TimeDelta(microseconds=1)


--- morrow/timezone.mojo ---

from .util import rjust
from ._libc import c_localtime

alias UTC_TZ = TimeZone(0, "UTC")


@value
struct TimeZone(Stringable):
    var offset: Int
    var name: String

    fn __init__(inout self, offset: Int, name: String = ""):
        self.offset = offset
        self.name = name

    fn __str__(self) -> String:
        return self.name

    fn is_none(self) -> Bool:
        return self.name == "None"

    @staticmethod
    fn none() -> TimeZone:
        return TimeZone(0, "None")

    @staticmethod
    fn local() -> TimeZone:
        var local_t = c_localtime(0)
        return TimeZone(local_t.tm_gmtoff.to_int(), "local")

    @staticmethod
    fn from_utc(utc_str: String) raises -> TimeZone:
        if len(utc_str) == 0:
            raise Error("utc_str is empty")
        if utc_str == "utc" or utc_str == "UTC" or utc_str == "Z":
            return TimeZone(0, "utc")
        var p = 3 if len(utc_str) > 3 and utc_str[0:3] == "UTC" else 0

        var sign = -1 if utc_str[p] == "-" else 1
        if utc_str[p] == "+" or utc_str[p] == "-":
            p += 1

        if (
            len(utc_str) < p + 2
            or not isdigit(ord(utc_str[p]))
            or not isdigit(ord(utc_str[p + 1]))
        ):
            raise Error("utc_str format is invalid")
        var hours: Int = atol(utc_str[p : p + 2])
        p += 2

        var minutes: Int
        if len(utc_str) <= p:
            minutes = 0
        elif len(utc_str) == p + 3 and utc_str[p] == ":":
            minutes = atol(utc_str[p + 1 : p + 3])
        elif len(utc_str) == p + 2 and isdigit(ord(utc_str[p])):
            minutes = atol(utc_str[p : p + 2])
        else:
            minutes = 0
            raise Error("utc_str format is invalid")
        var offset: Int = sign * (hours * 3600 + minutes * 60)
        return TimeZone(offset)

    fn format(self, sep: String = ":") -> String:
        var sign: String
        var offset_abs: Int
        if self.offset < 0:
            sign = "-"
            offset_abs = -self.offset
        else:
            sign = "+"
            offset_abs = self.offset
        var hh = offset_abs // 3600
        var mm = offset_abs % 3600
        return sign + rjust(hh, 2, "0") + sep + rjust(mm, 2, "0")


--- morrow/util.mojo ---

from collections.vector import DynamicVector

from .constants import MAX_TIMESTAMP, MAX_TIMESTAMP_MS, MAX_TIMESTAMP_US
from .constants import _DAYS_IN_MONTH, _DAYS_BEFORE_MONTH


fn _is_leap(year: Int) -> Bool:
    "year -> 1 if leap year, else 0."
    return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)


def _days_before_year(year: Int) -> Int:
    "year -> number of days before January 1st of year."
    var y = year - 1
    return y * 365 + y // 4 - y // 100 + y // 400


def _days_in_month(year: Int, month: Int) -> Int:
    "year, month -> number of days in that month in that year."
    if month == 2 and _is_leap(year):
        return 29
    return _DAYS_IN_MONTH[month]


def _days_before_month(year: Int, month: Int) -> Int:
    "year, month -> number of days in year preceding first day of month."
    if month > 2 and _is_leap(year):
        return _DAYS_BEFORE_MONTH[month] + 1
    return _DAYS_BEFORE_MONTH[month]


@always_inline
def _ymd2ord(year: Int, month: Int, day: Int) -> Int:
    "year, month, day -> ordinal, considering 01-Jan-0001 as day 1."
    dim = _days_in_month(year, month)
    return _days_before_year(year) + _days_before_month(year, month) + day


def normalize_timestamp(timestamp: Float64) -> Float64:
    """Normalize millisecond and microsecond timestamps into normal timestamps."""
    if timestamp > MAX_TIMESTAMP:
        if timestamp < MAX_TIMESTAMP_MS:
            timestamp /= 1000
        elif timestamp < MAX_TIMESTAMP_US:
            timestamp /= 1_000_000
        else:
            raise Error(
                "The specified timestamp " + String(timestamp) + "is too large."
            )
    return timestamp


fn _repeat_string(string: String, n: Int) -> String:
    var result: String = ""
    for _ in range(n):
        result += string
    return result


fn rjust(string: String, width: Int, fillchar: String = " ") -> String:
    var extra = width - len(string)
    return _repeat_string(fillchar, extra) + string


fn rjust(string: Int, width: Int, fillchar: String = " ") -> String:
    return rjust(String(string), width, fillchar)


--- test.mojo ---

from testing import assert_equal, assert_true

from morrow._libc import c_gettimeofday
from morrow._py import py_dt_datetime, py_time
from morrow import Morrow
from morrow import TimeZone
from morrow import TimeDelta


def assert_datetime_equal(dt: Morrow, py_dt: PythonObject):
    assert_true(
        dt.year == int(py_dt.year)
        and dt.month == int(py_dt.month)
        and dt.hour == int(py_dt.hour)
        and dt.minute == int(py_dt.minute)
        and dt.second == int(py_dt.second),
        "dt: " + str(dt) + " is not equal to py_dt: " + str(py_dt),
    )


def test_now():
    print("Running test_now()")
    var result = Morrow.now()
    assert_datetime_equal(result, py_dt_datetime().now())


def test_utcnow():
    print("Running test_utcnow()")
    var result = Morrow.utcnow()
    assert_datetime_equal(result, py_dt_datetime().utcnow())


def test_fromtimestamp():
    print("Running test_fromtimestamp()")
    var t = c_gettimeofday()
    var result = Morrow.fromtimestamp(t.tv_sec)
    assert_datetime_equal(result, py_dt_datetime().now())


def test_utcfromtimestamp():
    print("Running test_utcfromtimestamp()")
    var t = c_gettimeofday()
    var result = Morrow.utcfromtimestamp(t.tv_sec)
    assert_datetime_equal(result, py_dt_datetime().utcnow())


def test_iso_format():
    print("Running test_iso_format()")
    var d0 = Morrow(2023, 10, 1, 0, 0, 0, 1234)
    assert_equal(d0.isoformat(), "2023-10-01T00:00:00.001234")
    assert_equal(d0.isoformat(timespec="seconds"), "2023-10-01T00:00:00")
    assert_equal(d0.isoformat(timespec="milliseconds"), "2023-10-01T00:00:00.001")

    # with TimeZone
    var d1 = Morrow(2023, 10, 1, 0, 0, 0, 1234, TimeZone(28800, "Beijing"))
    assert_equal(d1.isoformat(timespec="seconds"), "2023-10-01T00:00:00+08:00")


def test_time_zone():
    print("Running test_time_zone()")
    assert_equal(TimeZone.from_utc("UTC+0800").offset, 28800)
    assert_equal(TimeZone.from_utc("UTC+08:00").offset, 28800)
    assert_equal(TimeZone.from_utc("UTC08:00").offset, 28800)
    assert_equal(TimeZone.from_utc("UTC0800").offset, 28800)
    assert_equal(TimeZone.from_utc("+08:00").offset, 28800)
    assert_equal(TimeZone.from_utc("+0800").offset, 28800)
    assert_equal(TimeZone.from_utc("08").offset, 28800)


def test_strptime():
    print("Running test_strptime()")
    m = Morrow.strptime("20-01-2023 15:49:10", "%d-%m-%Y %H:%M:%S", TimeZone.none())
    assert_equal(str(m), "2023-01-20T15:49:10.000000+00:00")

    m = Morrow.strptime("2023-10-18 15:49:10 +0800", "%Y-%m-%d %H:%M:%S %z")
    assert_equal(str(m), "2023-10-18T15:49:10.000000+08:00")

    m = Morrow.strptime("2023-10-18 15:49:10", "%Y-%m-%d %H:%M:%S", "+09:00")
    assert_equal(str(m), "2023-10-18T15:49:10.000000+09:00")


def test_ordinal():
    print("Running test_ordinal()")
    m = Morrow(2023, 10, 1)
    o = m.toordinal()
    assert_equal(o, 738794)

    m2 = Morrow.fromordinal(o)
    assert_equal(m2.year, 2023)
    assert_equal(m.month, 10)
    assert_equal(m.day, 1)


def test_sub():
    print("Running test_sub()")
    var result = Morrow(2023, 10, 1, 10, 0, 0, 1) - Morrow(2023, 10, 1, 10, 0, 0)
    assert_equal(result.microseconds, 1)
    assert_equal(str(result), "0:00:00000001")

    result = Morrow(2023, 10, 1, 10, 0, 1) - Morrow(2023, 10, 1, 10, 0, 0)
    assert_equal(result.seconds, 1)
    assert_equal(str(result), "0:00:01")

    result = Morrow(2023, 10, 1, 10, 1, 0) - Morrow(2023, 10, 1, 10, 0, 0)
    assert_equal(result.seconds, 60)
    assert_equal(str(result), "0:01:00")

    result = Morrow(2023, 10, 2, 10, 0, 0) - Morrow(2023, 10, 1, 10, 0, 0)
    assert_equal(result.days, 1)
    assert_equal(str(result), "1 day, 0:00:00")

    result = Morrow(2023, 10, 3, 10, 1, 1) - Morrow(2023, 10, 1, 10, 0, 0)
    assert_equal(result.days, 2)
    assert_equal(str(result), "2 days, 0:01:01")


def test_timedelta():
    print("Running test_timedelta()")
    assert_equal(TimeDelta(3, 2, 100).total_seconds(), 259202.0001)
    assert_true(
        TimeDelta(2, 1, 50).__add__(TimeDelta(1, 1, 50)).__eq__(TimeDelta(3, 2, 100))
    )
    assert_true(
        TimeDelta(3, 2, 100).__sub__(TimeDelta(2, 1, 50)).__eq__(TimeDelta(1, 1, 50))
    )
    assert_true(TimeDelta(3, 2, 100).__neg__().__eq__(TimeDelta(-3, -2, -100)))
    assert_true(TimeDelta(-3, -2, -100).__abs__().__eq__(TimeDelta(3, 2, 100)))
    assert_true(TimeDelta(1, 1, 50).__le__(TimeDelta(1, 1, 51)))
    assert_true(TimeDelta(1, 1, 50).__le__(TimeDelta(1, 1, 50)))
    assert_true(TimeDelta(1, 1, 50).__lt__(TimeDelta(1, 1, 51)))
    assert_true(not TimeDelta(1, 1, 50).__lt__(TimeDelta(1, 1, 50)))
    assert_true(TimeDelta(1, 1, 50).__ge__(TimeDelta(1, 1, 50)))
    assert_true(TimeDelta(1, 1, 50).__ge__(TimeDelta(1, 1, 49)))
    assert_true(not TimeDelta(1, 1, 50).__gt__(TimeDelta(1, 1, 50)))
    assert_true(TimeDelta(1, 1, 50).__gt__(TimeDelta(1, 1, 49)))
    assert_equal(
        str(
            TimeDelta(
                weeks=100,
                days=100,
                hours=100,
                minutes=100,
                seconds=100,
                microseconds=10000000,
                milliseconds=10000000000,
            )
        ),
        "919 days, 23:28:30",
    )


def test_from_to_py():
    print("Running test_from_to_py()")
    m = Morrow.now()
    dt = m.to_py()
    assert_datetime_equal(m, dt)

    m2 = Morrow.from_py(dt)
    assert_datetime_equal(m2, dt)


def test_format():
    print("Running test_format()")
    var m = Morrow(2024, 2, 1, 3, 4, 5, 123456)
    assert_equal(
        m.format("YYYY-MM-DD HH:mm:ss.SSS ZZ"), "2024-02-01 03:04:05.123 +00:00"
    )
    assert_equal(m.format("Y-YY-YYY-YYYY M-MM D-DD"), "Y-24--2024 2-02 1-01")
    assert_equal(m.format("H-HH-h-hh m-mm s-ss"), "3-03-3-03 4-04 5-05")
    assert_equal(m.format("S-SS-SSS-SSSS-SSSSS-SSSSSS"), "1-12-123-1234-12345-123456")
    assert_equal(m.format("d-dd-ddd-dddd"), "4--Thu-Thursday")
    assert_equal(m.format("YYYY[Y] [[]MM[]][M]"), "2024Y [02]M")


def main():
    test_now()
    test_utcnow()
    test_fromtimestamp()
    test_utcfromtimestamp()
    test_iso_format()
    test_sub()
    test_time_zone()
    test_strptime()
    test_timedelta()
    test_from_to_py()
    test_format()



--- .gitignore ---

.DS_Store

--- LICENSE ---

MIT License

Copyright (c) 2023 Lukas Hermann

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


--- README.md ---

# Mojo Shims

These are a collection of random utils I've written for use in my Mojo projects. Most are ported from Zig's standard library. Some of them are have quirks due to bugs in the language, and others due to my lack of testing. Nothing here is production code, and will probably break frequently as new language changes crop up.


--- __init__.mojo ---



--- bitcast/__init__.mojo ---

from math.bit import bswap
from sys.info import is_little_endian, sizeof


@always_inline
fn read_simd_native[
    T: DType
](owned bytes: SIMD[DType.uint8, sizeof[SIMD[T, 1]]()]) -> SIMD[T, 1]:
    let ptr = Pointer.address_of(bytes).bitcast[SIMD[T, 1]]()
    return ptr.load()


@always_inline
fn read_simd_foreign[
    T: DType
](owned bytes: SIMD[DType.uint8, sizeof[SIMD[T, 1]]()]) -> SIMD[T, 1]:
    return bswap[T, 1](read_simd_native[T](bytes))


@always_inline
fn from_le_bytes[
    T: DType
](owned bytes: SIMD[DType.uint8, sizeof[SIMD[T, 1]]()]) -> SIMD[T, 1]:
    @parameter
    if is_little_endian():
        return read_simd_native[T](bytes)
    else:
        return read_simd_foreign[T](bytes)


@always_inline
fn from_be_bytes[
    T: DType, N: Int
](owned bytes: SIMD[DType.uint8, sizeof[SIMD[T, 1]]()]) -> SIMD[T, 1]:
    @parameter
    if is_little_endian():
        return read_simd_foreign[T](bytes)
    else:
        return read_simd_native[T](bytes)


@always_inline
fn write_simd_native[
    T: DType
](owned value: SIMD[T, 1]) -> SIMD[DType.uint8, sizeof[SIMD[T, 1]]()]:
    let ptr = DTypePointer[T](Pointer.address_of[SIMD[T, 1]](value)).bitcast[
        DType.uint8
    ]()
    return ptr.simd_load[sizeof[SIMD[T, 1]]()](0)


@always_inline
fn write_simd_foreign[
    T: DType
](owned value: SIMD[T, 1]) -> SIMD[DType.uint8, sizeof[SIMD[T, 1]]()]:
    return bswap[DType.uint8, sizeof[SIMD[T, 1]]()](write_simd_native[T](value))


@always_inline
fn to_le_bytes[
    T: DType
](owned value: SIMD[T, 1]) -> SIMD[DType.uint8, sizeof[SIMD[T, 1]]()]:
    @parameter
    if is_little_endian():
        return write_simd_native[T](value)
    else:
        return write_simd_foreign[T](value)


@always_inline
fn to_be_bytes[
    T: DType
](owned value: SIMD[T, 1]) -> SIMD[DType.uint8, sizeof[SIMD[T, 1]]()]:
    @parameter
    if is_little_endian():
        return write_simd_foreign[T](value)
    else:
        return write_simd_native[T](value)


--- experiments/read_dir.mojo ---

"""
A port of the code from this stack overflow answer:
https://stackoverflow.com/a/4204758

```c
#include <dirent.h> 
#include <stdio.h> 

int main(void) {
  DIR *d;
  struct dirent *dir;
  d = opendir(".");
  if (d) {
    while ((dir = readdir(d)) != NULL) {
      printf("%s\n", dir->d_name);
    }
    closedir(d);
  }
  return(0);
}
```
"""
from memory.unsafe import Pointer
from sys.info import sizeof


@value
@register_passable("trivial")
struct DIR:
    pass


@value
@register_passable("trivial")
struct dirent:
    var d_ino: UInt64
    var d_off: UInt64
    var d_reclen: UInt16
    var d_type: UInt8
    var d_name: Pointer[UInt8]


@always_inline
fn closedir(arg: Pointer[DIR]) -> Int32:
    return external_call["closedir", Int32, Pointer[DIR]](arg)


@always_inline
fn opendir(arg: Pointer[UInt8]) -> Pointer[DIR]:
    return external_call["opendir", Pointer[DIR], Pointer[UInt8]](arg)


@always_inline
fn readdir(arg: Pointer[DIR]) -> Pointer[dirent]:
    return external_call["readdir", Pointer[dirent], Pointer[DIR]](arg)


@always_inline
fn fdopendir(arg: Int32) -> DIR:
    return external_call["fdopendir", DIR](arg)


# based on "https://github.com/crisadamo/mojo-libc/blob/main/Libc.mojo"
@always_inline
fn str_to_cstring(s: String) -> Pointer[UInt8]:
    let ptr = Pointer[UInt8].alloc(len(s) + 1)
    for i in range(len(s)):
        ptr.store(i, ord(s[i]))
    ptr.store(len(s), ord("\0"))
    return ptr


fn main():
    var dir = Pointer[dirent]()
    let path = str_to_cstring(".")
    let d = opendir(path)
    if d:
        while dir := readdir(d):
            let direntry = dir.load(0)
            # let dname = StringRef(
            #     direntry.d_name.bitcast[__mlir_type.`!pop.scalar<si8>`]().address,
            #     direntry.d_reclen.to_int(),
            # )
            let ptr = dir.bitcast[__mlir_type.`!pop.scalar<si8>`]().offset(
                sizeof[UInt64]() * 2 + sizeof[UInt16]() + sizeof[UInt8]()
            ).address
            let dname = StringRef(
                ptr,
                dir.load(0).d_reclen.to_int(),
            )
            print(dname)
        let _closed_ok = closedir(d)
    path.free()


--- file/__init__.mojo ---

from memory import memset
from math import min

from shims.libc.stdio import fopen, fread, fclose, fwrite
from shims.libc.string import strnlen

alias c_char = UInt8
alias FILE = UInt64
alias BUF_SIZE = 4096


fn to_char_ptr(s: String) -> Pointer[c_char]:
    """Only ASCII-based strings."""
    let ptr = Pointer[c_char]().alloc(len(s) + 1)
    for i in range(len(s)):
        ptr.store(i, ord(s[i]))
    ptr.store(len(s), ord("\0"))
    return ptr


struct File:
    var handle: Pointer[UInt64]
    var fname: Pointer[c_char]
    var mode: Pointer[c_char]

    fn __init__(inout self, filename: String, mode: StringLiteral):
        let fname = to_char_ptr(filename)

        let mode_cstr = to_char_ptr(mode)
        let handle = fopen(fname, mode_cstr)

        self.fname = fname
        self.mode = mode_cstr
        self.handle = handle

    fn __bool__(self) -> Bool:
        return self.handle.__bool__()

    fn __del__(owned self) raises:
        if self.handle:
            pass
            # TODO: uncomment when external_call resolution bug is fixed
            # let c = fclose(self.handle)
            # if c != 0:
            #     raise Error("Failed to close file")
        if self.fname:
            self.fname.free()
        if self.mode:
            self.mode.free()

    fn __moveinit__(inout self, owned other: Self):
        self.fname = other.fname
        self.mode = other.mode
        self.handle = other.handle
        other.handle = Pointer[FILE]()
        other.fname = Pointer[c_char]()
        other.mode = Pointer[c_char]()

    fn do_nothing(self):
        pass

    fn read[D: Dim](self, buffer: Buffer[D, DType.uint8]) raises -> Int:
        return fread(
            buffer.data.as_scalar_pointer(), sizeof[UInt8](), BUF_SIZE, self.handle
        ).to_int()

    fn write[D: Dim](self, buffer: Buffer[D, DType.uint8]) raises -> Int:
        return fwrite(
            buffer.data.as_scalar_pointer(), sizeof[UInt8](), len(buffer), self.handle
        ).to_int()

    fn write_all[D: Dim](self, buffer: Buffer[D, DType.uint8]) raises:
        var index = 0
        while index != len(buffer):
            index += self.write(buffer)

    fn write_byte(self, byte: UInt8) raises:
        let buf = Buffer[1, DType.uint8]().stack_allocation()
        buf[0] = byte
        self.write_all(buf)

    fn write_byte_n_times(self, byte: UInt8, n: Int) raises:
        var bytes = StaticTuple[256, UInt8]()
        let bytes_ptr = DTypePointer[DType.uint8](
            Pointer.address_of(bytes).bitcast[UInt8]()
        )
        memset[DType.uint8](
            bytes_ptr,
            byte,
            256,
        )
        var remaining = n
        while remaining > 0:
            let to_write = min(remaining, bytes.__len__())
            self.write_all(Buffer[Dim(), DType.uint8](bytes_ptr, to_write))
            remaining -= to_write


--- libc/__init__.mojo ---



--- libc/dirent/__init__.mojo ---

from memory.unsafe import Pointer


@value
@register_passable("trivial")
struct DIR:
    pass


@value
@register_passable("trivial")
struct dirent:
    var d_ino: UInt64
    var d_off: UInt64
    var d_reclen: UInt16
    var d_type: UInt8
    var d_name: Pointer[UInt8]


fn closedir(arg: Pointer[DIR]) -> Int32:
    return external_call["closedir", Int32, Pointer[DIR]](arg)


fn opendir(arg: Pointer[UInt8]) -> Pointer[DIR]:
    return external_call["opendir", Pointer[DIR], Pointer[UInt8]](arg)


fn readdir(arg: Pointer[DIR]) -> Pointer[dirent]:
    return external_call["readdir", Pointer[dirent], Pointer[DIR]](arg)


fn fdopendir(arg: Int32) -> DIR:
    return external_call["fdopendir", DIR](arg)


--- libc/stdio/__init__.mojo ---

from memory.unsafe import Pointer

alias FILE = UInt64


fn clearerr(arg: Pointer[FILE]) -> UInt8:
    return external_call["clearerr", UInt8, Pointer[FILE]](arg)


fn fclose(arg: Pointer[FILE]) -> Int32:
    return external_call["fclose", Int32, Pointer[FILE]](arg)


fn feof(arg: Pointer[FILE]) -> Int32:
    return external_call["feof", Int32, Pointer[FILE]](arg)


fn ferror(arg: Pointer[FILE]) -> Int32:
    return external_call["ferror", Int32, Pointer[FILE]](arg)


fn fflush(arg: Pointer[FILE]) -> Int32:
    return external_call["fflush", Int32, Pointer[FILE]](arg)


fn fgetc(arg: Pointer[FILE]) -> Int32:
    return external_call["fgetc", Int32, Pointer[FILE]](arg)


fn fopen(__filename: Pointer[UInt8], __mode: Pointer[UInt8]) -> Pointer[FILE]:
    return external_call["fopen", Pointer[FILE], Pointer[UInt8], Pointer[UInt8]](
        __filename, __mode
    )


fn fwrite(
    __ptr: Pointer[UInt8], __size: UInt64, __nitems: UInt64, __stream: Pointer[FILE]
) -> UInt64:
    return external_call[
        "fwrite", UInt64, Pointer[UInt8], UInt64, UInt64, Pointer[FILE]
    ](__ptr, __size, __nitems, __stream)


fn fread(
    __ptr: Pointer[UInt8], __size: UInt64, __nitems: UInt64, __stream: Pointer[FILE]
) -> UInt64:
    return external_call[
        "fread", UInt64, Pointer[UInt8], UInt64, UInt64, Pointer[FILE]
    ](__ptr, __size, __nitems, __stream)


--- libc/string/__init__.mojo ---

from memory.unsafe import Pointer


fn strnlen(pointer: Pointer[UInt8]) -> Int:
    return external_call["strnlen", Int, Pointer[UInt8]](pointer)


--- read/__init__.mojo ---

"""
This is a port of Zig's buffered reader.
See: https://github.com/ziglang/zig/blob/master/lib/std/io/buffered_reader.zig

Example usage:
==============

from shims.file import File
from shims.read import BufReader
from memory.buffer import Buffer


fn main() raises:
    let f = File("file.txt")
    var reader = BufReader[4096](f ^)
    let buf = Buffer[256, DType.uint8]().stack_allocation()
    var bytes_read = 1
    while bytes_read > 0:
        bytes_read = reader.read(buf)
        print(
            StringRef(
                buf.data.as_scalar_pointer()
                .bitcast[__mlir_type.`!pop.scalar<si8>`]()
                .address,
                bytes_read,
            )
        )
"""

from utils.list import Dim
from math import min, abs
from math.limit import max_finite
from memory import memcpy
from memory.buffer import Buffer
from memory.unsafe import Pointer, DTypePointer
from sys.info import sizeof
from utils.index import Index
from utils.vector import DynamicVector
import testing

from shims.file import File

# Types aliases
alias c_char = UInt8


struct BufReader[BUF_SIZE: Int]:
    var unbuffered_reader: File
    var data: DTypePointer[DType.uint8]
    var end: Int
    var start: Int

    fn __init__(inout self, owned reader: File):
        self.unbuffered_reader = reader ^
        self.data = DTypePointer[DType.uint8]().alloc(BUF_SIZE)
        self.end = 0
        self.start = 0

    fn __moveinit__(inout self, owned other: Self):
        self.unbuffered_reader = other.unbuffered_reader ^
        self.data = other.data
        self.end = other.end
        self.start = other.start
        other.unbuffered_reader = File("", "r")
        other.data = DTypePointer[DType.uint8]()

    fn read[D: Dim](inout self, dest: Buffer[D, DType.uint8]) raises -> Int:
        var dest_index = 0
        let buf = Buffer[BUF_SIZE, DType.uint8](self.data)

        while dest_index < len(dest):
            let written = min(len(dest) - dest_index, self.end - self.start)
            memcpy(dest.data.offset(dest_index), self.data.offset(self.start), written)
            if written == 0:
                # buf empty, fill it
                let n = self.unbuffered_reader.read(buf)
                if n == 0:
                    # reading from the unbuffered stream returned nothing
                    # so we have nothing left to read.
                    return dest_index
                self.start = 0
                self.end = n
            self.start += written
            dest_index += written
        return len(dest)

    fn do_nothing(self):
        pass


struct Reader[BUF_SIZE: Int]:
    var context: BufReader[BUF_SIZE]

    fn do_nothing(self):
        pass

    fn __init__(inout self, owned context: BufReader[BUF_SIZE]):
        self.context = context ^

    # Returns the number of bytes read. It may be less than buffer.len.
    # If the number of bytes read is 0, it means end of stream.
    # End of stream is not an error condition.
    fn read[D: Dim](inout self, buffer: Buffer[D, DType.uint8]) raises -> Int:
        return self.context.read(buffer)

    # Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
    # means the stream reached the end. Reaching the end of a stream is not an error
    # condition.
    fn read_all[D: Dim](inout self, buffer: Buffer[D, DType.uint8]) raises -> Int:
        return self.read_at_least(buffer, len(buffer))

    # Returns the number of bytes read, calling the underlying read
    # function the minimal number of times until the buffer has at least
    # `len` bytes filled. If the number read is less than `len` it means
    # the stream reached the end. Reaching the end of the stream is not
    # an error condition.
    fn read_at_least[
        D: Dim
    ](inout self, buffer: Buffer[D, DType.uint8], len: Int) raises -> Int:
        # assert(len <= buffer.len);
        var index: Int = 0
        while index < len:
            let amt = self.read(
                Buffer[Dim(), DType.uint8](
                    buffer.data.offset(index), buffer.__len__() - index
                )
            )
            if amt == 0:
                break
            index += amt
        return index

    # If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead.
    fn read_no_eof[D: Dim](inout self, buf: Buffer[D, DType.uint8]) raises:
        let amt_read = self.read_all(buf)
        if amt_read < len(buf):
            raise Error("Unexpected End of Stream.")

    # Appends to the `writer` contents by reading from the stream until `delimiter` is found.
    # Does not write the delimiter itself.
    # If `optional_max_size` is not null and amount of written bytes exceeds `optional_max_size`,
    # returns `error.StreamTooLong` and finishes appending.
    # If `optional_max_size` is null, appending is unbounded.
    fn stream_until_delimiter[
        BuffD: Dim
    ](
        inout self,
        inout writer: FixedBufferStream[BuffD],
        delimiter: UInt8,
        max_size: Int,
    ) raises:
        for i in range(max_size):
            let byte = self.read_byte()
            if byte == delimiter:
                return
            writer.write_byte(byte)
        raise Error("Stream too long")

    # Appends to the `writer` contents by reading from the stream until `delimiter` is found.
    # Does not write the delimiter itself.
    # If `optional_max_size` is not null and amount of written bytes exceeds `optional_max_size`,
    # returns `error.StreamTooLong` and finishes appending.
    # If `optional_max_size` is null, appending is unbounded.
    fn stream_until_delimiter[
        BuffD: Dim
    ](inout self, inout writer: FixedBufferStream[BuffD], delimiter: UInt8) raises:
        while True:
            let byte = self.read_byte()
            if byte == delimiter:
                return
            writer.write_byte(byte)

    # Reads 1 byte from the stream or returns `error.EndOfStream`.
    fn read_byte(inout self) raises -> UInt8:
        let result = Buffer[1, DType.uint8]().stack_allocation()
        let amt_read = self.read(result)
        if amt_read < 1:
            raise Error("End of stream")
        return result[0]


@value
struct FixedBufferStream[D: Dim]:
    var buffer: Buffer[D, DType.uint8]
    var pos: Int

    @always_inline
    fn __init__(inout self, buffer: Buffer[D, DType.uint8]):
        self.buffer = buffer
        self.pos = 0

    @always_inline
    fn read[
        DestDim: Dim
    ](inout self, destination: Buffer[DestDim, DType.uint8]) raises -> Int:
        let size = min(len(destination), len(self.buffer) - self.pos)
        let end = self.pos + size
        memcpy(destination.data, self.buffer.data.offset(self.pos), size)
        self.pos = end
        return size

    # If the returned number of bytes written is less than requested, the
    # buffer is full. Returns `error.NoSpaceLeft` when no bytes would be written.
    # Note: `error.NoSpaceLeft` matches the corresponding error from
    # `std.fs.File.WriteError`.
    fn write[BuffD: Dim](inout self, bytes: Buffer[BuffD, DType.uint8]) raises -> Int:
        if len(bytes) == 0:
            return 0
        if self.pos >= len(self.buffer):
            raise Error("No space left")

        let n = len(bytes) if self.pos + len(bytes) <= len(self.buffer) else len(
            self.buffer
        ) - self.pos

        memcpy(self.buffer.data.offset(self.pos), bytes.data, n)
        self.pos += n

        if n == 0:
            raise Error("No space left")

        return n

    fn write_all[BuffD: Dim](inout self, bytes: Buffer[BuffD, DType.uint8]) raises:
        var index = 0
        while index != len(bytes):
            index += self.write(
                Buffer[Dim(), DType.uint8](bytes.data.offset(index), len(bytes) - index)
            )

    fn write_byte(inout self, byte: UInt8) raises:
        let arr = Buffer[1, DType.uint8]().stack_allocation()
        self.write_all(arr)

    fn seek_to(inout self, pos: Int) raises:
        self.pos = min(len(self.buffer), pos)

    fn seek_by(inout self, amt: Int) raises:
        if amt < 0:
            let abs_amt = abs(amt)
            if abs_amt > self.pos:
                self.pos = 0
            else:
                self.pos -= abs_amt
        else:
            let new_pos = self.pos + amt
            self.pos = min(len(self.buffer), new_pos)

    @always_inline
    fn get_end_pos(self) -> Int:
        return len(self.buffer)

    @always_inline
    fn get_pos(self) -> Int:
        return self.pos

    @always_inline
    fn get_written(self) -> Buffer[Dim(), DType.uint8]:
        return Buffer[Dim(), DType.uint8](self.buffer.data, self.pos)

    @always_inline
    fn reset(inout self):
        self.pos = 0


--- siphash/__init__.mojo ---

"""
This is a port of Zig's SipHash
See: https://github.com/ziglang/zig/blob/master/lib/std/crypto/siphash.zig
"""

from memory.buffer import Buffer
from utils.list import Dim
from memory import memcpy, memset_zero
from memory.unsafe import Pointer, DTypePointer
from sys.info import sizeof
from math import rotate_bits_left
from utils.static_tuple import StaticTuple
from math import min
from algorithm.functional import parallelize
import testing

from shims.bitcast import from_be_bytes, from_le_bytes, to_le_bytes


@always_inline
fn slice_buf[T: DType, D: Dim](b: Buffer[D, T], slc: slice) -> Buffer[Dim(), T]:
    return Buffer[Dim(), T](b.data.offset(slc.start), slc.__len__())


@value
struct SipHashStateless[T: DType, C_ROUNDS: Int, D_ROUNDS: Int]:
    alias block_length = 64
    alias key_length = 16

    var v0: UInt64
    var v1: UInt64
    var v2: UInt64
    var v3: UInt64
    var msg_len: UInt8

    @always_inline
    fn __init__(inout self, key: Buffer[Self.key_length, DType.uint8]) raises:
        # constrained[T == DType.uint64 or T == DType.uint128]()
        constrained[T == DType.uint64]()
        constrained[C_ROUNDS > 0 and D_ROUNDS > 0]()

        let k0: UInt64 = from_le_bytes[DType.uint64](key.simd_load[sizeof[UInt64]()](0))
        let k1: UInt64 = from_le_bytes[DType.uint64](key.simd_load[sizeof[UInt64]()](8))

        self.v0 = k0 ^ UInt64(0x736F6D6570736575)
        self.v1 = k1 ^ UInt64(0x646F72616E646F6D)
        self.v2 = k0 ^ UInt64(0x6C7967656E657261)
        self.v3 = k1 ^ UInt64(0x7465646279746573)
        self.msg_len = UInt8(0)

        #   @parameter if T == DType.128:
        #       self.v1 ^= 0xee;

    @always_inline
    fn update[D: Dim](inout self, b: Buffer[D, DType.uint8]):
        # std.debug.assert(b.len % 8 == 0);

        for off in range(0, len(b), 8):
            let blob = b.simd_load[8](off)
            self.round(blob)

        self.msg_len += UInt8(len(b))

    fn final[D: Dim](inout self, b: Buffer[D, DType.uint8]) -> SIMD[T, 1]:
        #       constrained[len(b) < 8](b.len < 8);
        self.msg_len += UInt8(len(b))

        let buf = Buffer[8, DType.uint8]().stack_allocation()
        memset_zero(buf.data, 8)

        memcpy(buf.data, b.data, len(b))
        buf[7] = self.msg_len
        self.round(buf.simd_load[8](0))

        @parameter
        if T == DType.uint64:
            self.v2 ^= 0xFF
        else:
            self.v2 ^= 0xEE

        @unroll
        for _ in range(D_ROUNDS):
            self.sip_round()

        let b1 = (self.v0 ^ self.v1 ^ self.v2 ^ self.v3).to_int()
        return SIMD[T, 1](b1)

        ## TODO: remove above return when UInt128 is available

        # @parameter if T == dtype.uint64:
        #     return b1

        # self.v1 ^= 0xdd;

        # @unroll
        # for _ in range(D_ROUNDS):
        #     self.sip_round()

        # let b2 = self.v0 ^ self.v1 ^ self.v2 ^ self.v3;
        # return (UInt128(b2) << 64) | b1

    @always_inline
    fn round(inout self, owned b: SIMD[DType.uint8, 8]):
        var m_buf = SIMD[DType.uint8, sizeof[UInt64]()]()

        @unroll
        for i in range(8):
            m_buf[i] = b[i]
        let m = from_le_bytes[DType.uint64](m_buf)
        self.v3 ^= m

        @unroll
        for i in range(C_ROUNDS):
            self.sip_round()
        self.v0 ^= m

    @always_inline
    fn sip_round(inout self):
        self.v0 += self.v1
        self.v1 = rotate_bits_left[13](self.v1)
        self.v1 ^= self.v0
        self.v0 = rotate_bits_left[32](self.v0)
        self.v2 += self.v3
        self.v3 = rotate_bits_left[16](self.v3)
        self.v3 ^= self.v2
        self.v0 += self.v3
        self.v3 = rotate_bits_left[21](self.v3)
        self.v3 ^= self.v0
        self.v2 += self.v1
        self.v1 = rotate_bits_left[17](self.v1)
        self.v1 ^= self.v2
        self.v2 = rotate_bits_left[32](self.v2)

    @staticmethod
    fn hash[
        MsgD: Dim
    ](
        msg: Buffer[MsgD, DType.uint8], key: Buffer[Self.key_length, DType.uint8]
    ) raises -> SIMD[T, 1]:
        let aligned_len = len(msg) - (len(msg) % 8)
        var c = Self(key)
        c.update(slice_buf(msg, slice(0, aligned_len)))
        return c.final(slice_buf(msg, slice(aligned_len, len(msg))))


@value
struct SipHash[T: DType, C_ROUNDS: Int, D_ROUNDS: Int]:
    alias key_length = 16
    alias mac_length = sizeof[SIMD[T, 1]]()
    alias block_length = 8

    alias State = SipHashStateless[T, C_ROUNDS, D_ROUNDS]
    var state: Self.State
    var ptr: DTypePointer[DType.uint8]
    var buf: Buffer[8, DType.uint8]
    var buf_len: Int

    fn __init__(inout self, key: Buffer[Self.key_length, DType.uint8]) raises:
        # constrained[T == DType.uint64 or T== DType.uint128]()
        constrained[T == DType.uint64]()
        constrained[C_ROUNDS > 0 and D_ROUNDS > 0]()
        self.state = SipHashStateless[T, C_ROUNDS, D_ROUNDS](key)
        self.ptr = DTypePointer[DType.uint8]().alloc(8)
        self.buf = Buffer[8, DType.uint8](self.ptr)
        self.buf_len = 0

    fn update(inout self, b: Buffer[Dim(), DType.uint8]):
        """
        Add data to the state.
        """

        var off = 0

        if self.buf_len != 0 and self.buf_len + len(b) >= 8:
            off += 8 - self.buf_len
            memcpy(self.buf.data.offset(self.buf_len), b.data, off)
            self.state.update(self.buf)
            self.buf_len = 0

        let remain_len = len(b) - off
        let aligned_len = remain_len - (remain_len % 8)
        self.state.update(slice_buf(b, slice(off, off + aligned_len)))

        let b_slice = slice_buf(b, slice(off + aligned_len, len(b)))
        memcpy(self.buf.data.offset(self.buf_len), b_slice.data, len(b_slice))
        self.buf_len += len(b_slice)

    fn peek(self) -> Buffer[Self.mac_length, DType.uint8]:
        var copy = self
        return copy.final_result()

    fn final(inout self, out: Buffer[Self.mac_length, DType.uint8]):
        """
        Return an authentication tag for the current state
        Assumes `out` is less than or equal to `mac_length`.
        """
        let s = self.state.final(slice_buf(self.buf, slice(0, self.buf_len)))
        let bytes = to_le_bytes(s)
        out.simd_store[Self.mac_length](0, bytes)

    fn final_result(inout self) -> Buffer[Self.mac_length, DType.uint8]:
        let result = Buffer[Self.mac_length, DType.uint8]().stack_allocation()
        self.final(result)
        return result

    @staticmethod
    fn create(
        out: Buffer[Self.mac_length, DType.uint8],
        msg: Buffer[Dim(), DType.uint8],
        key: Buffer[Self.key_length, DType.uint8],
    ) raises:
        """
        Return an authentication tag for a message and a key.
        """

        var ctx = Self(key)
        ctx.update(msg)
        ctx.final(out)

    fn final_int(inout self) -> SIMD[T, 1]:
        """
        Return an authentication tag for the current state, as an integer.
        """

        return self.state.final(slice_buf(self.buf, slice(0, self.buf_len)))

    @staticmethod
    fn to_int(
        msg: Buffer[Dim(), DType.uint8],
        key: Buffer[Self.key_length, DType.uint8],
    ) raises -> SIMD[T, 1]:
        """
        Return an authentication tag for a message and a key, as an integer.
        """

        return Self.State.hash(msg, key)

    fn __del__(owned self):
        self.ptr.free()


alias UInt8x8 = SIMD[DType.uint8, 8]


alias SipHash24 = SipHash[DType.uint64, 2, 4]

alias TEST_DATA_LEN = 63
alias TEST_DATA: StaticTuple[TEST_DATA_LEN, SIMD[DType.uint8, 8]] = StaticTuple[
    63, SIMD[DType.uint8, 8]
](
    SIMD[DType.uint8, 8](49, 14, 14, 221, 71, 219, 111, 114),
    SIMD[DType.uint8, 8](253, 103, 220, 147, 197, 57, 248, 116),
    SIMD[DType.uint8, 8](90, 79, 169, 217, 9, 128, 108, 13),
    SIMD[DType.uint8, 8](45, 126, 251, 215, 150, 102, 103, 133),
    SIMD[DType.uint8, 8](183, 135, 113, 39, 224, 148, 39, 207),
    SIMD[DType.uint8, 8](141, 166, 153, 205, 100, 85, 118, 24),
    SIMD[DType.uint8, 8](206, 227, 254, 88, 110, 70, 201, 203),
    SIMD[DType.uint8, 8](55, 209, 1, 139, 245, 0, 2, 171),
    SIMD[DType.uint8, 8](98, 36, 147, 154, 121, 245, 245, 147),
    SIMD[DType.uint8, 8](176, 228, 169, 11, 223, 130, 0, 158),
    SIMD[DType.uint8, 8](243, 185, 221, 148, 197, 187, 93, 122),
    SIMD[DType.uint8, 8](167, 173, 107, 34, 70, 47, 179, 244),
    SIMD[DType.uint8, 8](251, 229, 14, 134, 188, 143, 30, 117),
    SIMD[DType.uint8, 8](144, 61, 132, 192, 39, 86, 234, 20),
    SIMD[DType.uint8, 8](238, 242, 122, 142, 144, 202, 35, 247),
    SIMD[DType.uint8, 8](229, 69, 190, 73, 97, 202, 41, 161),
    SIMD[DType.uint8, 8](219, 155, 194, 87, 127, 204, 42, 63),
    SIMD[DType.uint8, 8](148, 71, 190, 44, 245, 233, 154, 105),
    SIMD[DType.uint8, 8](156, 211, 141, 150, 240, 179, 193, 75),
    SIMD[DType.uint8, 8](189, 97, 121, 167, 29, 201, 109, 187),
    SIMD[DType.uint8, 8](152, 238, 162, 26, 242, 92, 214, 190),
    SIMD[DType.uint8, 8](199, 103, 59, 46, 176, 203, 242, 208),
    SIMD[DType.uint8, 8](136, 62, 163, 227, 149, 103, 83, 147),
    SIMD[DType.uint8, 8](200, 206, 92, 205, 140, 3, 12, 168),
    SIMD[DType.uint8, 8](148, 175, 73, 246, 198, 80, 173, 184),
    SIMD[DType.uint8, 8](234, 184, 133, 138, 222, 146, 225, 188),
    SIMD[DType.uint8, 8](243, 21, 187, 91, 184, 53, 216, 23),
    SIMD[DType.uint8, 8](173, 207, 107, 7, 99, 97, 46, 47),
    SIMD[DType.uint8, 8](165, 201, 29, 167, 172, 170, 77, 222),
    SIMD[DType.uint8, 8](113, 101, 149, 135, 102, 80, 162, 166),
    SIMD[DType.uint8, 8](40, 239, 73, 92, 83, 163, 135, 173),
    SIMD[DType.uint8, 8](66, 195, 65, 216, 250, 146, 216, 50),
    SIMD[DType.uint8, 8](206, 124, 242, 114, 47, 81, 39, 113),
    SIMD[DType.uint8, 8](227, 120, 89, 249, 70, 35, 243, 167),
    SIMD[DType.uint8, 8](56, 18, 5, 187, 26, 176, 224, 18),
    SIMD[DType.uint8, 8](174, 151, 161, 15, 212, 52, 224, 21),
    SIMD[DType.uint8, 8](180, 163, 21, 8, 190, 255, 77, 49),
    SIMD[DType.uint8, 8](129, 57, 98, 41, 240, 144, 121, 2),
    SIMD[DType.uint8, 8](77, 12, 244, 158, 229, 212, 220, 202),
    SIMD[DType.uint8, 8](92, 115, 51, 106, 118, 216, 191, 154),
    SIMD[DType.uint8, 8](208, 167, 4, 83, 107, 169, 62, 14),
    SIMD[DType.uint8, 8](146, 89, 88, 252, 214, 66, 12, 173),
    SIMD[DType.uint8, 8](169, 21, 194, 155, 200, 6, 115, 24),
    SIMD[DType.uint8, 8](149, 43, 121, 243, 188, 10, 166, 212),
    SIMD[DType.uint8, 8](242, 29, 242, 228, 29, 69, 53, 249),
    SIMD[DType.uint8, 8](135, 87, 117, 25, 4, 143, 83, 169),
    SIMD[DType.uint8, 8](16, 165, 108, 245, 223, 205, 154, 219),
    SIMD[DType.uint8, 8](235, 117, 9, 92, 205, 152, 108, 208),
    SIMD[DType.uint8, 8](81, 169, 203, 158, 203, 163, 18, 230),
    SIMD[DType.uint8, 8](150, 175, 173, 252, 44, 230, 102, 199),
    SIMD[DType.uint8, 8](114, 254, 82, 151, 90, 67, 100, 238),
    SIMD[DType.uint8, 8](90, 22, 69, 178, 118, 213, 146, 161),
    SIMD[DType.uint8, 8](178, 116, 203, 142, 191, 135, 135, 10),
    SIMD[DType.uint8, 8](111, 155, 180, 32, 61, 231, 179, 129),
    SIMD[DType.uint8, 8](234, 236, 178, 163, 11, 34, 168, 127),
    SIMD[DType.uint8, 8](153, 36, 164, 60, 193, 49, 87, 36),
    SIMD[DType.uint8, 8](189, 131, 141, 58, 175, 191, 141, 183),
    SIMD[DType.uint8, 8](11, 26, 42, 50, 101, 213, 26, 234),
    SIMD[DType.uint8, 8](19, 80, 121, 163, 35, 28, 230, 96),
    SIMD[DType.uint8, 8](147, 43, 40, 70, 228, 215, 6, 102),
    SIMD[DType.uint8, 8](225, 145, 95, 92, 177, 236, 164, 108),
    SIMD[DType.uint8, 8](243, 37, 150, 92, 161, 109, 98, 159),
    SIMD[DType.uint8, 8](87, 95, 242, 142, 96, 56, 27, 229),
    SIMD[DType.uint8, 8](114, 69, 6, 235, 76, 50, 138, 149),
)


fn test_siphash64_2_4(test_key: Buffer[16, DType.uint8]) raises:
    let msg = Buffer[64, DType.uint8]().stack_allocation()
    for i in range(TEST_DATA_LEN):
        msg[i] = i

    @parameter
    fn test_inner(i: Int):
        let out = Buffer[SipHash24.mac_length, DType.uint8]().stack_allocation()
        try:
            SipHash24.create(out, slice_buf(msg, slice(0, i)), test_key)
        except:
            print("failed on test: ", i)
        let out_str = String(
            StringRef(out.data.bitcast[DType.int8]().address, SipHash24.mac_length)
        )
        var vector = TEST_DATA[i]
        let vector_str = String(
            StringRef(
                Pointer.address_of[SIMD[DType.uint8, 8]](vector)
                .bitcast[__mlir_type.`!pop.scalar<si8>`]()
                .address,
                SipHash24.mac_length,
            )
        )
        if not testing.assert_equal(vector_str, out_str):
            pass

    parallelize[test_inner](TEST_DATA_LEN)


fn test_iterative_non_divisible_update() raises:
    alias BUF_LEN = 1024
    let buf = Buffer[BUF_LEN, DType.uint8]().stack_allocation()

    @unroll
    for i in range(BUF_LEN):
        buf[i] = i

    let key_str = String("0x128dad08f12307")
    let key = Buffer[SipHash24.key_length, DType.uint8]().stack_allocation()

    for i in range(SipHash24.key_length):
        key[i] = ord(key_str[i])

    let end = 9
    for _ in range(end, len(buf), 9):
        let non_iterative_hash = SipHash24.to_int(slice_buf(buf, slice(0, end)), key)
        var siphash = SipHash24(key)
        for i in range(0, end, 7):
            siphash.update(slice_buf(buf, slice(i, min(i + 7, end))))
        let iterative_hash = siphash.final_int()
        if not testing.assert_equal(iterative_hash, non_iterative_hash):
            raise Error("failed")


fn run_tests() raises:
    # Test vectors from reference implementation.
    # https://github.com/veorq/SipHash/blob/master/vectors.h
    let bufptr = DTypePointer[DType.uint8]().alloc(16)
    let test_key = Buffer[
        16,
        DType.uint8,
    ](bufptr)
    test_key.simd_store[16](
        0,
        SIMD[DType.uint8, 16](
            0x00,
            0x01,
            0x02,
            0x03,
            0x04,
            0x05,
            0x06,
            0x07,
            0x08,
            0x09,
            0xA,
            0xB,
            0xC,
            0x0D,
            0x0E,
            0x0F,
        ),
    )

    test_siphash64_2_4(test_key)
    test_iterative_non_divisible_update()


--- write/__init__.mojo ---

"""
A port of Zig's BufferedReader.

Example:
========

from shims.file import File
from shims.read import BufReader
from shims.write import BufWriter
from memory.buffer import Buffer


fn main() raises:
    let f = File("a.txt", "r")
    let out_f = File("a2.txt", "w+")
    var reader = BufReader[4096](f ^)
    var writer = BufWriter[4096](out_f ^)
    let buf = Buffer[256, DType.uint8]().stack_allocation()
    var bytes_read = 1
    while bytes_read > 0:
        bytes_read = reader.read(buf)
        if bytes_read > 0:
            print(
                StringRef(
                    buf.data.as_scalar_pointer()
                    .bitcast[__mlir_type.`!pop.scalar<si8>`]()
                    .address,
                    bytes_read,
                )
            )
            let write_buf = Buffer[Dim(), DType.uint8](buf.data, bytes_read)
            let bytes_written = writer.write(write_buf)
            _ = bytes_written

"""
from shims.file import File
from memory import memcpy


struct BufWriter[BUF_SIZE: Int]:
    var unbuffered_writer: File
    var data: DTypePointer[DType.uint8]
    var end: Int

    fn __init__(inout self, owned writer: File):
        self.unbuffered_writer = writer ^
        self.data = DTypePointer[DType.uint8]().alloc(BUF_SIZE)
        self.end = 0

    fn __del__(owned self) raises:
        self.flush()

    fn flush(inout self) raises:
        self.unbuffered_writer.write_all(
            Buffer[Dim(), DType.uint8](self.data, self.end)
        )
        self.end = 0

    fn write[D: Dim](inout self, bytes: Buffer[D, DType.uint8]) raises -> Int:
        if self.end + len(bytes) > BUF_SIZE:
            self.flush()
            if len(bytes) > BUF_SIZE:
                return self.unbuffered_writer.write(bytes)
        let new_end = self.end + len(bytes)
        memcpy(self.data.offset(self.end), bytes.data, new_end - self.end)
        self.end = new_end
        return len(bytes)

    fn write(inout self, str: StringRef) raises -> Int:
        var strbuf = DynamicVector[UInt8]()
        for i in range(len(str)):
            strbuf.push_back(ord(str[i]))
        let buf = Buffer[Dim(), DType.uint8](strbuf.data, len(strbuf))
        return self.write(buf)


--- wyhash/__init__.mojo ---

"""
A port of Zig's Wyhash
See: https://github.com/ziglang/zig/blob/master/lib/std/hash/wyhash.zig
"""
from memory.unsafe import DTypePointer, Pointer
from memory import memcpy, memset_zero, stack_allocation
from utils.static_tuple import StaticTuple
from utils.list import Dim
import testing

from shims.bitcast import from_le_bytes

alias UInt64x4 = SIMD[DType.uint64, 4]


@always_inline
fn mum(inout a: UInt64, inout b: UInt64):
    let x = _umul128(a, b)
    a = x[0]
    b = x[1]


@always_inline
fn mix(a_: UInt64, b_: UInt64) -> UInt64:
    var a = a_
    var b = b_
    mum(a, b)
    return a ^ b


@always_inline
fn _umul128(multiplier: UInt64, multiplicand: UInt64) -> SIMD[DType.uint64, 2]:
    """Taken from stack overflow.
    https://stackoverflow.com/a/46923106
    """
    # multiplier   = ab = a * 2^32 + b
    # multiplicand = cd = c * 2^32 + d
    # ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d
    let a = multiplier >> 32
    let b = multiplier & 0xFFFFFFFF
    let c = multiplicand >> 32
    let d = multiplicand & 0xFFFFFFFF

    # let ac = a * c
    let ad = a * d
    # let bc = b * c
    let bd = b * d

    let adbc = ad + (b * c)
    let adbc_carry = 1 if adbc < ad else 0

    # multiplier * multiplicand = product_hi * 2^64 + product_lo
    let product_lo = bd + (adbc << 32)
    let product_lo_carry = 1 if product_lo < bd else 0
    let product_hi = (a * c) + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry

    return SIMD[DType.uint64, 2](product_hi, product_lo)


fn test_umul128() raises:
    let a: UInt64
    let b: UInt64
    a, b = UInt64(0x0FFFFFFFFFFFFFFF), UInt64(0xF0000000)
    # testing.assert_equal(
    #     _umul128(a, b), SIMD[DType.uint64, 2](0xEFFFFFF, 0xFFFFFFFF10000000)
    # )


struct Wyhash:
    var _secret: UInt64x4
    var a: UInt64
    var b: UInt64

    # we only care about the first three values
    var state: StaticTuple[3, UInt64]
    var total_len: Int

    var buf: StaticTuple[48, UInt8]
    var buf_len: Int

    fn __init__(inout self, seed: UInt64):
        self._secret = SIMD[DType.uint64, 4](
            0xA0761D6478BD642F,
            0xE7037ED1A0B428DB,
            0x8EBC6AF09C88C6E3,
            0x589965CC75374CC3,
        )
        self.a = 0
        self.b = 0
        self.buf_len = 0
        self.total_len = 0
        self.state = StaticTuple[3, UInt64]()
        self.buf = StaticTuple[48, UInt8]()
        memset_zero(Pointer.address_of(self.buf).bitcast[UInt8](), 48)

        self.state[0] = seed ^ mix(seed ^ self._secret[0], self._secret[1])
        self.state[1] = self.state[0]
        self.state[2] = self.state[0]

    @always_inline
    fn __copyinit__(inout self, other: Self):
        self.a = other.a
        self.b = other.b
        self.state = other.state
        self.total_len = other.total_len
        self.buf_len = 0
        self.buf = StaticTuple[48, UInt8]()
        self._secret = UInt64x4(
            0xA0761D6478BD642F,
            0xE7037ED1A0B428DB,
            0x8EBC6AF09C88C6E3,
            0x589965CC75374CC3,
        )

    # This is subtly different from other hash function update calls. Wyhash requires the last
    # full 48-byte block to be run through final1 if is exactly aligned to 48-bytes.
    @always_inline
    fn update[D: Dim](inout self, input: Buffer[D, DType.uint8]):
        self.total_len += len(input)

        if len(input) <= 48 - self.buf_len:
            memcpy(
                DTypePointer[DType.uint8](
                    Pointer.address_of(self.buf).bitcast[UInt8]().offset(self.buf_len)
                ),
                input.data,
                len(input),
            )
            self.buf_len += len(input)
            return

        var i = 0
        if self.buf_len > 0:
            i = 48 - self.buf_len
            memcpy(
                Pointer.address_of(self.buf).bitcast[UInt8]().offset(self.buf_len),
                input.data,
                i,
            )
            self.round(
                Buffer[48, DType.uint8](Pointer.address_of(self.buf).bitcast[UInt8]())
            )
            self.buf_len = 0

        for i in range(i, len(input), 48):
            self.round(
                Buffer[48, DType.uint8](input.data.offset(i).as_scalar_pointer())
            )

        let remaining_bytes = Buffer[Dim(), DType.uint8](
            input.data.offset(i), len(input) - i
        )
        if len(remaining_bytes) < 16 and i >= 48:
            let rem = 16 - len(remaining_bytes)
            memcpy(
                Pointer.address_of(self.buf)
                .bitcast[UInt8]()
                .offset(self.buf.__len__() - rem),
                input.data.offset(i - rem),
                i,
            )
        memcpy(
            Pointer.address_of(self.buf).bitcast[UInt8](),
            remaining_bytes.data.as_scalar_pointer(),
            len(remaining_bytes),
        )
        self.buf_len = len(remaining_bytes)

    @always_inline
    fn final(inout self) -> UInt64:
        let input_ptr = Pointer.address_of(self.buf).bitcast[UInt8]()
        var input = Buffer[Dim(), DType.uint8](input_ptr, self.buf_len)
        var new_self = self  # ensure idempotency

        if self.total_len <= 16:
            new_self.small_key(input)
        else:
            var offset: Int = 0
            if self.buf_len < 16:
                var scratch = StaticTuple[16, UInt8]()
                let scratch_pointer = Pointer.address_of(scratch).bitcast[UInt8]()
                let buf_ptr = Pointer.address_of(self.buf).bitcast[UInt8]()
                let rem = 16 - self.buf_len
                memcpy(scratch_pointer, buf_ptr.offset(self.buf.__len__() - rem), rem)
                memcpy(scratch_pointer.offset(rem), buf_ptr, self.buf_len)

                # Same as input but with additional bytes preceeding start in case of a short buffer
                input = Buffer[Dim(), DType.uint8](scratch_pointer, 16)
                offset = rem

            new_self.final0()
            new_self.final1(input, offset)

        return new_self.final2()

    @staticmethod
    fn hash[D: Dim](seed: UInt64, input: Buffer[D, DType.uint8]) -> UInt64:
        var self = Self(seed)

        if len(input) <= 16:
            self.small_key(input)
        else:
            var i: Int = 0
            if len(input) >= 48:
                while i + 48 < len(input):
                    self.round(Buffer[48, DType.uint8](input.data.offset(i)))
                    i += 48
                self.final0()
            self.final1(input, i)

        self.total_len = len(input)
        return self.final2()

    @always_inline
    fn small_key[D: Dim](inout self, input: Buffer[D, DType.uint8]):
        # constrained[D.get() <= 16]()
        if len(input) >= 4:
            let end = len(input) - 4
            let quarter = (len(input) >> 3) << 2

        #     self.a = (
        #         UInt64(
        #             (
        #                 from_le_bytes[DType.uint32](
        #                     Buffer[sizeof[UInt32](), DType.uint8](input.data)
        #                 )
        #             ).to_int()
        #         )
        #         << 32
        #     ) | UInt64(
        #         from_le_bytes[DType.uint32](
        #             Buffer[sizeof[UInt32](), DType.uint8](input.data.offset(quarter))
        #         ).to_int()
        #     )
        #     self.b = (
        #         UInt64(
        #             (
        #                 from_le_bytes[DType.uint32](
        #                     Buffer[sizeof[UInt32](), DType.uint8](
        #                         input.data.offset(end)
        #                     )
        #                 )
        #             ).to_int()
        #         )
        #         << 32
        #     ) | UInt64(
        #         from_le_bytes[DType.uint32](
        #             Buffer[sizeof[UInt32](), DType.uint8](
        #                 input.data.offset(end - quarter)
        #             )
        #         ).to_int()
        #     )
        # elif len(input) > 0:
        #     self.a = (
        #         (UInt64(input[0].to_int()) << 16)
        #         | (UInt64(input[len(input) >> 1].to_int()) << 8)
        #         | UInt64(input[len(input) - 1].to_int())
        #     )
        #     self.b = 0
        # else:
        #     self.a = 0
        #     self.b = 0

    fn round(inout self, input: Buffer[48, DType.uint8]):
        @unroll
        for i in range(3):
            # let a = from_le_bytes[DType.uint64](
            #     Buffer[sizeof[UInt64](), DType.uint8](input.data.offset(8 * (2 * i)))
            # )
            # let b = from_le_bytes[DType.uint64](
            #     Buffer[sizeof[UInt64](), DType.uint8](
            #         input.data.offset(8 * (2 * i + 1))
            #     )
            # )
            # self.state[i] = mix(a ^ self._secret[i + 1], b ^ self.state[i])
            pass

    @always_inline
    fn final0(inout self):
        self.state[0] ^= self.state[1] ^ self.state[2]

    #  input_lb must be at least 16-bytes long (in shorter key cases the smallKey function will be
    #  used instead). We use an index into a slice to for comptime processing as opposed to if we
    #  used pointers.
    fn final1[D: Dim](inout self, input_lb: Buffer[D, DType.uint8], start_pos: Int):
        # constrained(input_lb.len >= 16);
        # constrained(input_lb.len - start_pos <= 48);
        let input = Buffer[Dim(), DType.uint8](
            input_lb.data.offset(start_pos), len(input_lb) - start_pos
        )

        for i in range(0, len(input), 16):
            pass
            # self.state[0] = mix(
            #     from_le_bytes[DType.uint64](
            #         Buffer[sizeof[UInt64](), DType.uint8](
            #             input.data.offset(i), len(input) - i
            #         )
            #     )
            #     ^ self._secret[1],
            #     from_le_bytes[DType.uint64](
            #         Buffer[sizeof[UInt64](), DType.uint8](
            #             input.data.offset(i + 8), len(input) - i + 8
            #         )
            #     )
            #     ^ self.state[0],
            # )

        # self.a = from_le_bytes[DType.uint64](
        #     Buffer[sizeof[UInt64](), DType.uint8](
        #         input_lb.data.offset(len(input_lb) - 16)
        #     )
        # )
        # self.b = from_le_bytes[DType.uint64](
        #     Buffer[sizeof[UInt64](), DType.uint8](
        #         input_lb.data.offset(len(input_lb) - 8)
        #     )
        # )

    @always_inline
    fn final2(inout self) -> UInt64:
        self.a ^= self._secret[1]
        print(self._secret[1])
        self.b ^= self.state[0]
        mum(self.a, self.b)
        return mix(self.a ^ self._secret[0] ^ self.total_len, self.b ^ self._secret[1])



--- README.md ---

# Mojo with Qt6

Install mojo and python.

```sh-session
$ curl -s https://get.modular.com | sh -
$ /opt/homebrew/bin/python3.11 -m venv venv
$ source venv/bin/activate
$ pip install PySide6
$ mojo qt.mojo
```

The demo launches a QML UI from mojo, and calls a mojo fn from QML when the button is clicked.


--- app.qml ---

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

Window {
    width: 300
    height: 200
    visible: true
    title: "Hello Mojo"

    ColumnLayout {
        anchors.fill:  parent

        Text {
            id: text
            text: "Hello World"
            Layout.alignment: Qt.AlignHCenter
        }
        Button {
            text: "Click me"
            Layout.alignment: Qt.AlignHCenter
            onClicked: on_clicked.invoke()
        }
    }
}


--- experiments/callback.mojo ---

from python import Python

fn callback() -> Int:
    print("mojo callback running")
    return 42

fn main() raises:
    Python.add_to_path("./")
    var callback_mod = Python.import_module("callback")    
    print("calling python callback from mojo")
    var fn_address = UnsafePointer[fn () -> Int].address_of(callback).bitcast[Int64]()[0]
    callback_mod.callback(fn_address)


--- experiments/callback.py ---

import ctypes

def callback(mojo_fn_address):
    print(f"in python callback, address {mojo_fn_address}")
    prototype = ctypes.CFUNCTYPE(ctypes.c_int)
    mojo_func = prototype(mojo_fn_address)
    print("calling mojo fn from python")
    result = mojo_func()
    print(f"result {result}")


--- experiments/widget.mojo ---

import sys
from python import Python

fn main() raises:
    var QtWidgets = Python.import_module("PySide6.QtWidgets")    
    var argv: PythonObject = [sys.argv()[0]]
    var app = QtWidgets.QApplication(argv)
    var button = QtWidgets.QPushButton("Hello")
    button.resize(100, 100)
    button.show()
    sys.exit(app.exec())


--- experiments/widget.py ---

import sys
from PySide6.QtWidgets import QApplication, QPushButton

if __name__ == "__main__":
    app = QApplication(sys.argv)
    button = QPushButton("Hello")
    button.resize(100, 100)
    button.show()
    sys.exit(app.exec())


--- experiments/window.mojo ---

import sys
from python import Python

fn main() raises:
    var QtGui = Python.import_module("PySide6.QtGui")    
    var argv: PythonObject = [sys.argv()[0]]
    var app = QtGui.QGuiApplication(argv)
    var window = QtGui.QWindow()
    window.setTitle("Mojo window")
    window.resize(100, 100)
    window.requestActivate()
    window.show()
    print("size", window.size(), "active", window.isActive(), "winid", window.winId())
    sys.exit(app.exec())
    _ = window


--- experiments/window.py ---

import sys
from PySide6.QtGui import QGuiApplication, QWindow

if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    window = QWindow()
    window.setTitle("Python window")
    window.resize(100, 100)
    window.requestActivate()
    window.show()
    print("size", window.size(), "active", window.isActive(), "winid", window.winId())
    sys.exit(app.exec())


--- invoke.py ---

import ctypes
from PySide6.QtCore import QObject, Slot

prototype = ctypes.CFUNCTYPE(ctypes.c_void_p)

class Invoke(QObject):
    def __init__(self, fn_address):
        super().__init__()
        self.mojo_fn = prototype(fn_address)

    @Slot()
    def invoke(self):
        self.mojo_fn()


--- qt.mojo ---

import sys
from python import Python

fn on_clicked():
    print("mojo on_clicked called from Python")

fn main() raises:
    Python.add_to_path("./")
    var QtGui = Python.import_module("PySide6.QtGui")    
    var QtQml = Python.import_module("PySide6.QtQml")    
    var invoke = Python.import_module("invoke")

    var fn_address = UnsafePointer[fn () -> None].address_of(on_clicked).bitcast[Int64]()[0]
    var invoke_on_clicked = invoke.Invoke(fn_address)

    var argv: PythonObject = [sys.argv()[0]]
    var app = QtGui.QGuiApplication(argv)
    var engine = QtQml.QQmlApplicationEngine()
    engine.rootContext().setContextProperty("on_clicked", invoke_on_clicked)
    engine.load("app.qml")
    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec())
    _ = engine
    _ = invoke_on_clicked



--- LICENSE ---

                                 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.


--- README.md ---

<br/>
<p align="center">
  <a href="https://github.com/alainrollejr/mocodes">
    <img src="https://github.com/alainrollejr/mocodes/blob/main/mocodeslogo.png" alt="Logo" width="200" height="200">
  </a>

  <h1 align="center">MoCodes</h1>

  <p align="center">
    An Error Correction (De)Coding library in pure Mojo 🔥
  </p>
</p>




## About The Project

MoCodes is a stand-alone Error Correction (De)Coding framework that leverages the power of Mojo.

As [discussed](https://docs.modular.com/mojo/why-mojo) by Modular, Mojo is a language for the future of AI development. Built on top of MLIR technology, rather than existing GCC and LLVM approaches, Mojo looks and feels like Python code, yet performs much closer to languages like Rust or C++. 

Error Correction Codes are being used in domains such as Wireless Communication and Quantum Computing. They are known to be very compute intensive, so much so that until recently they were implemented in dedicated ASIC silicon or programmed on FPGA accelerators.

In recent years with the advent of wide-vector SIMD CPU architectures and affordable yet powerful GPU cores they have been also implemented in C++ with a lot of vendor-specific intrinsics on CPU or with CUDA on GPU as well.

About time then to take a stab at how well Mojo lives up to the challenge (and how well the authors live up to the challenge of understanding how Mojo is meant to be used)

## Benchmark

We've been on a voyage of  exploration to find out how platform-independent frameworks originally meant for machine learning can be repurposed for error correction decoding. We have had reasonable results on GPU and TPU but not until Mojo came along we've reached decent throughputs on CPU. Results ofc vary with platform specifics, we have tried Intel, AMD and Macbook M3.

![ldpc_benchmark](https://github.com/alainrollejr/mocodes/blob/main/mocodesbenchmark.png)

While that looks awesome we estimate that there is a performance gap of about a factor 2 yet to be closed wrt C++ code that uses vendor specific intrinsics eg from the avx instruction set on Intel. Game on !

For now we only support generic (ir)regular  LDPC codes with *embarrassingly parallel* flooding batch decoding. We have committed just one example (1512 x 1872) LDPC Parity Check Matrix to this repo. This sparse parity check matrix has 7092 non-zero elements and is shown hereafter.

![ldpc_pcm](https://github.com/alainrollejr/mocodes/blob/main/codebook/example_pcm.png)

For now, this parity check matrix gets translated to look-up tables by an offline scipy script that takes an .npz file as input. The look-up tables get stored in the /codebook/ subdirectory.


## Quick Start

Try out the LDPC benchmark for yourself, on your own platform:

```
mojo build ldpcdec.mojo
```
```
./ldpcdec
```
You can tweak the following parameters in the main() function of [types.mojo](https://github.com/alainrollejr/mocodes/blob/main/types.mojo): *intra_codeword_parallellism_factor, ncodewordperthread, nthread*. Currently committed defaults seem to be close to optimal regardless the platform we have tried. 



## Roadmap

### v1.0 ✅
- [x] support for irregular LDPC decoding
- [x] support for (batch) early stopping


### v1.1 (WIP)
- [ ] Improve throughput (target: factor 2) by community expertise injection 
- [ ] Add a serving functionality (preferably gRPC based, ideally leveraging MAX serving)
- [ ] Add profiling and proper benchmarking tests


### v1.2
- [ ] incorporate generation of Look-Up Tables in the mojo code, such that the .npz file becomes the only configuration input that defines the code
- [ ] add an LDPC encoder
- [ ] add a script to simulate and visualise BER and BLER curves
- [ ] Autotuning and related features

### Longer Term
- [ ] Add polar codes
- [ ] Add Reed-Solomon codes
- [ ] Add CRC check codes
- [ ] Add layered LDPC decoding



## Contributing

The way we set this repo up should allow Mojo experts to contribute without necessarily being Error Correction Coding specialists.
Notably, the LDPC heavy lifting is done by a handful of functions in [types.mojo](https://github.com/alainrollejr/mocodes/blob/main/ldpc/types.mojo), i.e. *fn all_Lqij()* and *fn cnpu()*.
Memory load and store at this point seem to determine the throughput so all tips and tricks to speed up that memory access would much appreciated, along with any other improvements that can be spotted by expert *Mojicans*. 

If you are considering larger contributions, feel free to contact us for a smoother communication channel on Discord. If you find a bug or have an idea for a feature, please use our issue tracker. Before creating a new issue, please:
* Check if the issue already exists. If an issue is already reported, you can contribute by commenting on the existing issue.
* If not, create a new issue and include all the necessary details to understand/recreate the problem or feature request.

### Creating A Pull Request

1. Fork the Project
2. Create your Feature Branch
3. Commit your Changes
4. Push to the Branch
5. Open a Pull Request
> Once your changes are pushed, navigate to your fork on GitHub. And create a pull request against the original  repository.
> - Before creating a PR make sure the functional text output of ./ldpcdec is the same as the one on the main branch, i.e the batch syndrome on the all-zeros codewords should stay zero and the logit output values should all still equal 127.
> - In the pull request, provide a detailed description of the changes and why they're needed. Link any relevant issues.


## License

Distributed under the Apache 2.0 License.

## Acknowledgements

* Built with [Mojo](https://github.com/modularml/mojo) created by [Modular](https://github.com/modularml)



--- codebook/__init__.mojo ---




--- codebook/luts.mojo ---

alias nnz = 7092
alias nc = 1872
alias cnpu_sizes: VariadicList[Int]=VariadicList (3, 4, 5, 6, 8, 10)
alias ncnpu = 6
alias cnpu_indices: VariadicList[Int]=VariadicList (0, 216, 936, 1260, 1368, 1440)
alias vnpu_sizes: VariadicList[Int]=VariadicList (22, 23, 10, 5, 14, 7, 13, 6, 8, 9, 16, 9, 12, 1)
alias nvnpu = 14
alias vnpu_indices: VariadicList[Int]=VariadicList (0, 36, 72, 108, 180, 216, 252, 288, 324, 360, 396, 432, 468, 504)


--- codebook/repeats.bin ---









































































																																																																								

--- ldpc/__init__.mojo ---




--- ldpc/types.mojo ---

from math import trunc, mod,min,abs,max
from memory.unsafe import DTypePointer
from sys.info import simdwidthof
from python import Python
from python.object import PythonObject
from random import rand, randint
from algorithm import vectorize,  parallelize
from testing import assert_true
from tensor import Tensor, TensorSpec, TensorShape
from sys.intrinsics import strided_load, PrefetchOptions
from sys.info import num_physical_cores, num_logical_cores, num_performance_cores
from collections import List
from collections.vector import InlinedFixedVector


alias dtype = DType.int8
alias nelts = 2*simdwidthof[dtype]()  # The SIMD vector width, for some reason overbooking by factor 2 helps

alias max_allowed = SIMD[DType.int16,nelts](127)
alias min_allowed = SIMD[DType.int16,nelts](-127)



@parameter
fn all_cnpu_compiled(inout ip: Tensor3D, nnz: Int, ncnpu: Int, cnpu_sizes: VariadicList[Int],cnpu_indices: VariadicList[Int]) raises:        
    var slice_start : Int = 0       

    for idx in range(ncnpu):
        var slice_stop  : Int
        var n = cnpu_sizes[idx]
        if (idx == (ncnpu - 1)):
            slice_stop = nnz
        else:
            slice_stop  = slice_start + (cnpu_indices[idx+1] - cnpu_indices[idx])*n
        var block = ip.get_reference_to_row_slice(slice_start, slice_stop)
        # block.print()
        # print("cnpu [",slice_start,":",slice_stop,"] reshape to ",n)
        block.reshape_dim0(n)
        block.cnpu()
        block.flatten_dim2()
        slice_start = slice_stop

@parameter
fn all_syndromes_compiled(inout ip: Tensor3D,nnz: Int, ncnpu: Int, cnpu_sizes: VariadicList[Int], cnpu_indices: VariadicList[Int]) raises:        
    var slice_start : Int = 0       

    for idx in range(ncnpu):
        var slice_stop  : Int
        var n = cnpu_sizes[idx]
        if (idx == (ncnpu - 1)):
            slice_stop = nnz
        else:
            slice_stop  = slice_start + (cnpu_indices[idx+1] - cnpu_indices[idx])*n
        var block = ip.get_reference_to_row_slice(slice_start, slice_stop)
        # block.print()
        # print("cnpu [",slice_start,":",slice_stop,"] reshape to ",n)
        block.reshape_dim0(n)
        block.syndromes()
        block.flatten_dim2()
        slice_start = slice_stop

@parameter
fn all_Lqij(inout ip: Tensor3D, llr: Tensor3D, start_codeword_idx: Int, intra_codeword_parallellism_factor: Int,nc: Int,rep_indices: Tensor[DType.uint16], rep_offsets: Tensor[DType.uint16], r2c_indices: Tensor[DType.uint16]) raises:        
    # this version just loops over columns of the virtual column-first stored LLRs

    # below prefetch lines compile but don't seem to make a performance difference
    # alias r_opt = PrefetchOptions().high_locality()
    # llr.data().prefetch[r_opt]()
    # ip.data().prefetch[r_opt]()

       

    @parameter
    fn vnpu(column_idx:Int):
        var n_simd = rep_indices[column_idx]
        var n = n_simd.to_int()
        var slice_start = rep_offsets[column_idx].to_int()
        

        var lut_idx_cache = InlinedFixedVector[Int](n)
        
        for j in range(0,n):
            var m = r2c_indices[slice_start + j].to_int()
            lut_idx_cache[j] = m

        @parameter
        fn vp[nelts_local: Int](k: Int):
            
            # if n > 1:
            var sum = llr.load[nelts](column_idx, k+start_codeword_idx)                                
            var sum_int16 = sum.cast[DType.int16]()
            
            for j in range(0,n):
                var m = lut_idx_cache[j]
                var el = ip.load[nelts](m, k)  
                var el_16 =  el.cast[DType.int16]() 
                sum_int16 += el_16
                
            
            var tmp = sum_int16
            
            for j in range(n):
                # var m = r2c_indices[slice_start+j]
                var m = lut_idx_cache[j]
                                    
                # TODO: find a way to avoid the second load from memory, perhaps by making use of memory > buffer struct
                var el = ip.load[nelts](m, k)
                tmp  = sum_int16 - el.cast[DType.int16]()
                
                tmp = tmp.min(max_allowed)
                tmp = tmp.max(min_allowed)

                var tmp_dtype = tmp.cast[dtype]()

                ip.store[nelts](m,k,tmp_dtype) 



        vectorize[vp, nelts](ip.dim1)
        #vectorize_unroll[nelts,8,vp](ip.dim1) # unroll factor ideally set to ip.dim1//nelts
    parallelize[vnpu](nc,intra_codeword_parallellism_factor)

@parameter
fn all_LQ(inout ip: Tensor3D, llr: Tensor3D, inout op: Tensor3D, start_codeword_idx: Int, intra_codeword_parallellism_factor: Int,nc: Int, ncodeword: Int, rep_indices: Tensor[DType.uint16], rep_offsets: Tensor[DType.uint16], r2c_indices: Tensor[DType.uint16]) raises:        

    @parameter
    fn vnpu(codebit_idx:Int):  # equivalent of : "for codebit_idx in range(nc):"
        var n = rep_indices[codebit_idx].to_int()
        var slice_start = rep_offsets[codebit_idx].to_int()

        var lut_idx_cache = InlinedFixedVector[Int](n)
        for j in range(0,n):
            var m = r2c_indices[slice_start+j].to_int()
            lut_idx_cache[j] = m

        @parameter
        fn vp[nelts_local: Int](k: Int): # equivalent of : "for k in range(ncodeword):"" but we are taking nelts = simdwidth codewords at a time
            var sum = llr.load[nelts](codebit_idx, k+start_codeword_idx)
            var tmp = sum.cast[DType.int16]()
            for j in range(0,n):
                var m = lut_idx_cache[j]
                var el = ip.load[nelts](m, k)                  
                tmp += el.cast[DType.int16]()               
           

            tmp = tmp.min(max_allowed)
            tmp = tmp.max(min_allowed)

            var tmp_dtype = tmp.cast[dtype]()

            @unroll
            for j in range(nelts): # subtoptimal, should be strided store but syntax eludes me a bit 
                op.store[1](start_codeword_idx+k+j,codebit_idx,tmp_dtype[j])            

        vectorize[vp, nelts](ncodeword)
        #vectorize_unroll[nelts,8,vp](ip.dim1) # unroll factor ideally set to ip.dim1//nelts
    parallelize[vnpu](nc,intra_codeword_parallellism_factor)

@parameter
fn all_LQ_for_syndrome(inout ip: Tensor3D, llr: Tensor3D, inout op: Tensor3D, start_codeword_idx: Int, intra_codeword_parallellism_factor: Int,nc: Int,  rep_indices:Tensor[DType.uint16], rep_offsets: Tensor[DType.uint16], r2c_indices: Tensor[DType.uint16]) raises:        

    @parameter
    fn vnpu(column_idx:Int):  # equivalent of : "for column_idx in range(nc):"
        var n = rep_indices[column_idx].to_int()
        var slice_start = rep_offsets[column_idx].to_int()

        var lut_idx_cache = InlinedFixedVector[Int](n)
        for j in range(0,n):
            var m = r2c_indices[slice_start+j].to_int()
            lut_idx_cache[j] = m

        @parameter
        fn vp[nelts_local: Int](k: Int):
            # TODO: if n == 1 we can at once set the result to zero across the board
            # also TODO : don't load the same values twice, create a local array of SIMD for the n values
            var sum = llr.load[nelts](column_idx, k+start_codeword_idx)
            var sum_int16 = sum.cast[DType.int16]()
            for j in range(0,n):
                var m = lut_idx_cache[j]
                var el = ip.load[nelts](m, k)                  
                sum_int16 += el.cast[DType.int16]()
                
            var tmp = sum_int16
            for j in range(n):
                var m = lut_idx_cache[j]

                tmp = tmp.min(max_allowed)
                tmp = tmp.max(min_allowed)

                var tmp_dtype = tmp.cast[dtype]()

                op.store[nelts](m,k,tmp_dtype)                       

        vectorize[vp, nelts](ip.dim1)
    parallelize[vnpu](nc,intra_codeword_parallellism_factor)

           


@parameter
fn decode_compiled_v5[ base_niter: Int, max_niter: Int](ip : Tensor3D, inout op : Tensor3D, inout batch_syndrome: Int, intra_codeword_parallellism_factor: Int, start_codeword_idx: Int, nc: Int,nnz: Int, ncnpu: Int, nvnpu: Int, ncodeword: Int, rep_indices: Tensor[DType.uint16], rep_offsets: Tensor[DType.uint16], c2r_indices: Tensor[DType.uint16], r2c_indices: Tensor[DType.uint16], vnpu_sizes: VariadicList[Int], vnpu_indices: VariadicList[Int], cnpu_sizes: VariadicList[Int], cnpu_indices: VariadicList[Int]) raises:
    #print("decode_compiled_v5 on start_codeword_idx = ",start_codeword_idx)
    var ip_llr_rep = repeat_compiled_offset(ip, start_codeword_idx,rep_indices, nnz, ncodeword)
    ip_llr_rep.set_num_workers(intra_codeword_parallellism_factor)
    
    var syndrome_scratchpad = Tensor3D(nnz, ncodeword)
    syndrome_scratchpad.set_num_workers(intra_codeword_parallellism_factor)

    # first iteration, declares the variables
    var reliabilities = c2r(ip_llr_rep,c2r_indices) # a permuted copy  # ip_llr_rep_rfirst  
    reliabilities.set_num_workers(intra_codeword_parallellism_factor)

    all_cnpu_compiled(reliabilities,nnz, ncnpu, cnpu_sizes,cnpu_indices) # modifies in-place  
    all_Lqij(reliabilities, ip,start_codeword_idx,intra_codeword_parallellism_factor,nc,rep_indices,rep_offsets,r2c_indices) # modifies in-place

    # remaining iterations
    #@unroll
    for idx in range(1,base_niter-1):
        all_cnpu_compiled(reliabilities,nnz, ncnpu, cnpu_sizes,cnpu_indices) # modifies in-place    
        all_Lqij(reliabilities, ip,start_codeword_idx,intra_codeword_parallellism_factor,nc,rep_indices,rep_offsets,r2c_indices) # modifies in-place
        

    # final iteration
    all_cnpu_compiled(reliabilities,nnz, ncnpu, cnpu_sizes,cnpu_indices) # modifies in-place    
    all_LQ(reliabilities, ip, op, start_codeword_idx, intra_codeword_parallellism_factor,nc,ncodeword,rep_indices,rep_offsets,r2c_indices) # modifies in-place    
    
    # batch syndrome computation  (inefficient, but leaves reliabilities untouched for continued iteration if must be )
    all_LQ_for_syndrome(reliabilities, ip, syndrome_scratchpad, start_codeword_idx,intra_codeword_parallellism_factor,nc,rep_indices,rep_offsets,r2c_indices) # modifies syndrome_scratchpad in-place            
    all_syndromes_compiled(syndrome_scratchpad, nnz, ncnpu, cnpu_sizes, cnpu_indices) 
    
    batch_syndrome = syndrome_scratchpad.reduce_syndrome()

    var other_niter = max_niter - base_niter
    if other_niter > 0:
        if batch_syndrome != 0:
            all_Lqij(reliabilities, ip,start_codeword_idx,intra_codeword_parallellism_factor,nc,rep_indices,rep_offsets,r2c_indices) # modifies in-place

            for idx in range(1,other_niter-1):
                all_cnpu_compiled(reliabilities,nnz, ncnpu, cnpu_sizes,cnpu_indices) # modifies in-place    
                all_Lqij(reliabilities, ip,start_codeword_idx,intra_codeword_parallellism_factor,nc,rep_indices,rep_offsets,r2c_indices) # modifies in-place
                

            # final iteration
            all_cnpu_compiled(reliabilities,nnz, ncnpu, cnpu_sizes,cnpu_indices) # modifies in-place    
            all_LQ(reliabilities, ip, op, start_codeword_idx, intra_codeword_parallellism_factor,nc,ncodeword,rep_indices,rep_offsets,r2c_indices) # modifies in-place    
            
            # batch syndrome computation  (inefficient, but leaves reliabilities untouched for continued iteration if must be )
            all_LQ_for_syndrome(reliabilities, ip, syndrome_scratchpad, start_codeword_idx,intra_codeword_parallellism_factor,nc,rep_indices,rep_offsets,r2c_indices) # modifies syndrome_scratchpad in-place            
            all_syndromes_compiled(syndrome_scratchpad, nnz, ncnpu, cnpu_sizes, cnpu_indices) 
            
            batch_syndrome = syndrome_scratchpad.reduce_syndrome()



fn parallel_decode[base_niter: Int, max_niter: Int](ip : Tensor3D, inout op : Tensor3D, inout batch_syndrome: Int, intra_codeword_parallellism_factor: Int, nthread: Int, nc: Int,nnz: Int, ncnpu: Int, nvnpu: Int, ncodewordperthread: Int, rep_indices: Tensor[DType.uint16], rep_offsets: Tensor[DType.uint16], c2r_indices: Tensor[DType.uint16], r2c_indices: Tensor[DType.uint16], vnpu_sizes: VariadicList[Int], vnpu_indices: VariadicList[Int], cnpu_sizes: VariadicList[Int], cnpu_indices: VariadicList[Int]) raises:
    var syndromes = InlinedFixedVector[Int](nthread)
    
    @parameter
    fn decode_slice(s: Int):
        var slice_syndrome: Int = -1
        try:
            decode_compiled_v5[base_niter, max_niter](ip, op, slice_syndrome, intra_codeword_parallellism_factor, s*ncodewordperthread, nc,nnz, ncnpu, nvnpu, ncodewordperthread, rep_indices, rep_offsets, c2r_indices, r2c_indices, vnpu_sizes, vnpu_indices, cnpu_sizes,cnpu_indices)
            syndromes[s] = slice_syndrome
        except:
            print("try failed on decode_compiled_v5")

    parallelize[decode_slice](nthread, nthread)

    # if any of the slices had a failed syndrome, then the whole batch syndrome is flagged as failed
    batch_syndrome = 0
    for i in range(nthread):
        # print("slice ",i,": syndrome",syndromes[i])
        if syndromes[i] > 0:
            batch_syndrome = 1
            
   

# repeats the first dimension according to repeat indices input
@parameter
fn repeat_compiled_offset(inpv: Tensor3D, start_codeword_idx: Int, indices: Tensor[DType.uint16], total_repeats: Int, ncodeword: Int)  -> Tensor3D:       
    var Z = Tensor3D(total_repeats, ncodeword)
    @parameter
    fn rep[nelts: Int](k: Int):
        var idx = 0
        for j in range(inpv.dim0):
            var x =  inpv.load[nelts](j, k+start_codeword_idx)  
            var nrep = indices[j].to_int()
            for n in range(nrep): 
                Z.store[nelts](idx,k,x)
                idx += 1

    vectorize[rep, nelts](ncodeword)        
    return Z 







# permutes the first dimension according to given indices
@parameter
fn c2r(inpv: Tensor3D, c2r_indices: Tensor[DType.uint16])  -> Tensor3D:
    var Z = Tensor3D(inpv.dim0, inpv.dim1)
    @parameter
    fn gthr_c2r[nelts: Int](k: Int):
        for j in range(inpv.dim0):                 
            var idx = c2r_indices[j].to_int()
            var x =  inpv.load[nelts](idx, k)                    
            Z.store[nelts](j,k,x)

    vectorize[gthr_c2r, nelts](inpv.dim1)        
    return Z

@always_inline
fn pe[nelts_pe: Int](x: SIMD[dtype,nelts_pe], y: SIMD[dtype,nelts_pe]) -> SIMD[dtype,nelts_pe]:
    var ax = abs(x)
    var ay = abs(y)
    var m = min(ax,ay)
    var sx = x < 0
    var sy = y < 0
    var sd = sx == sy
    var v = sd.select(m,-m)
    return v




struct Tensor3D:
    var dim0: Int
    var dim1: Int
    var dim2: Int # optional 3rd dimension
    var dimprod: Int # dim0 * dim1
    var num_workers: Int
    var _data: DTypePointer[dtype]
    #var _tensor: Tensor[dtype]
    alias simd_width: Int = nelts

    fn __init__(inout self, *dims: Int):
        self.dim0 = dims[0]
        self.dim1 = dims[1]
        self.dimprod = self.dim1*self.dim0
        if len(dims) > 2:
            self.dim2 = dims[2]
        else:
            self.dim2 = 1
        var size = self.dim0 * self.dim1 * self.dim2
        self.num_workers = num_logical_cores()
        self._data = DTypePointer[dtype].alloc(size)
        
        #self._tensor = Tensor[dtype](self.dim0, self.dim2, self.dim1) # note the swap of dim1 and dim2, for now needed to align interpretations ??
        #self._data = self._tensor.data()

        # random initialisation in the interval centered on +16, mimicking roughly an all-zeros codeword plus mild noise
        # 

    fn rand_init(inout self):
        var size = self.dim0 * self.dim1 * self.dim2
        randint(self._data, size,-2,10)

        
    fn __copyinit__(inout self, other: Self):
        #self._tensor = other._tensor
        self._data = other._data
        self.dim0 = other.dim0
        self.dim1 = other.dim1
        self.dim2 = other.dim2
        self.dimprod = other.dimprod
        self.num_workers = other.num_workers
        

    # Initialize taking a pointer, don't set any elements
    fn __init__(
        inout self, data: DTypePointer[dtype],*dims: Int
    ):
        self._data = data
        self.num_workers = 1
        self.dim0 = dims[0]
        self.dim1 = dims[1]
        self.dimprod = self.dim1*self.dim0
        if len(dims) > 2:
            self.dim2 = dims[2]
        else:
            self.dim2 = 1

 
    
    fn set_num_workers(inout self, num_workers: Int):
        self.num_workers = num_workers

    fn free(inout self):
        self._data.free()


    # add a third dimension and arteficially reduce the first dimension to new_dim
    # note no actual data is being moved
    fn reshape_dim0(inout self, new_dim0: Int) raises:
        assert_true((self.dim0 % new_dim0) == 0, "reshape new_dim0 must divide original dim0")
        var f = self.dim0 // new_dim0
        self.dim2 = f * self.dim2
        self.dim0 = new_dim0
        self.dimprod = self.dim1*self.dim0

    # this is the 'undo' operation of reshape_dim0
    # note no actual data is being moved
    fn flatten_dim2(inout self):
        self.dim0 = self.dim0 * self.dim2
        self.dim2 = 1
        self.dimprod = self.dim1*self.dim0

    # the below code is not safe unless run as last thing in the program, when the
    # Tensor3D is no longer used or needed. Otherwise we can get following error:
    # double free or corruption (top)
    fn save(self, fpath: String) raises  -> String: 
        var spec = TensorSpec(dtype, self.dim0, self.dim1)
        var _tensor = Tensor[dtype](self._data,spec)       
        _tensor.tofile(fpath)
        print('File saved:',fpath)
        return fpath


    #@staticmethod
    fn load(inout self, fpath:String) raises:
        var load_mat = Tensor[dtype].fromfile(fpath) 
        var size = self.dim0 * self.dim1 * self.dim2       
        assert_true(load_mat.num_elements() == size,"nr of elements in file do not match self.num_elements()")
        memcpy(self._data,load_mat.data(),size)
        _ = load_mat # dunno why this line would be needed tbh, nicked it from https://www.modular.com/blog/whats-new-in-mojo-sdk-v0-5

        
    fn reduce_syndrome(self) -> Int: 
        for j in range(self.dim0):
            for k in range(self.dim1):                 
                var v = self.load[1](j, k)
                if v > 0:
                    return 1
        return 0
        
    

    @always_inline
    fn __getitem__(self, j: Int, k: Int) -> SIMD[dtype,1]:
        return self._data.load[width=1](j * self.dim1 + k)

    @always_inline
    fn __getitem__(self, j: Int, k: Int, l: Int) -> SIMD[dtype,1]:
        return self._data.load[width=1](j * self.dim1 + l*self.dimprod + k)

    fn data(self) -> DTypePointer[dtype]:
        return self._data

    @always_inline
    fn get_reference_to_row_slice(inout self,slice_start: Int, slice_end: Int) -> Self:
        var slice_len = slice_end - slice_start
        var src_ptr = self._data.offset(slice_start*self.dim1)
        return Self(src_ptr,slice_len,self.dim1)
    


    fn print[maxel_x: Int = 3, maxel_y: Int = 3](self)->None:
        var rank:Int = 2
        var dim0:Int = 0
        var dim1:Int = 0
        var dim2:Int = 0
        var val:SIMD[dtype, 1]=0
        

        if self.dim0 == 1:
            rank = 1
            dim0 = 1
            dim1 = self.dim1
            dim2 = self.dim2
        else:
            dim0 = self.dim0
            dim1 = self.dim1
            dim2 = self.dim2
        if dim0>0 and dim1>0:
            for l in range(dim2):
                print("Page: ",l)
                for j in range(dim0):
                    if (j < maxel_x) | (j > (dim0 - (maxel_x+1))):
                        if rank>1:
                            if j==0:
                                print("  [",end="")
                            else:
                                print("\n   ",end="")
                        print("[",end="")
                        for k in range(dim1):
                            if rank==1:
                                val = self[j,k,l]
                            if rank==2:
                                val = self[j,k,l]
                            if k==0:
                                print(val,end="")
                            elif (k < maxel_y) | (k > (dim1 - (maxel_y+1))):
                                print("  ",val,end="")
                            elif k == maxel_y:
                                print("...",end="")

                        print("]",end="")
                    elif j == maxel_x:
                        print()
                        print("    ...     ")
                if rank>1:
                    print("]",end="")
                print()


            print()
            # if rank>2:
            #     print("]")
        print("  Tensor3D:",self.dim0,'x',self.dim1,'x',self.dim2,",","DType:", dtype.__str__())
        print()

    fn print_simd(self):
        for j in range(self.dim0):
            @parameter
            fn pr[nelts: Int](k: Int):
                print(self.load[nelts](j, k))            

            vectorize[pr, nelts](self.dim1)

    @always_inline
    fn load[nelts: Int](self, j: Int, k: Int) -> SIMD[dtype, nelts]:
        return self._data.load[width=nelts](j * self.dim1 + k)

    @always_inline
    fn store[nelts: Int](self, j: Int, k: Int, val: SIMD[dtype, nelts]):
        return self._data.store[width=nelts](j * self.dim1 + k, val)

    

    # variants in case the 3D view on the data is used
    @always_inline
    fn load[nelts: Int](self, j: Int, k: Int, l: Int) -> SIMD[dtype, nelts]:
        return self._data.load[width=nelts](j * self.dim1 + l*self.dimprod + k)

    @always_inline
    fn store[nelts: Int](self, j: Int, k: Int, l: Int, val: SIMD[dtype, nelts]):
        return self._data.store[width=nelts](j * self.dim1 + l*self.dimprod + k, val)

        
    

    # perform a syndrome_N operation whereby N is assumed to equal dim0
    fn syndromes(inout self):
        @parameter
        fn calc_page(l:Int):
            @parameter
            fn sd[nelts: Int](k: Int):
                # perform xor of all sign bits
                var v = self.load[nelts](0, k,l)
                var ok = v*0
                var nok = ok + 1
                var tmp = v < 0
                for j in range(1,self.dim0):                 
                    tmp = tmp ^ (self.load[nelts](j, k,l) < 0)
                # at this point tmp shall have False values where the parity checks hold (xor of all sign bits should be zero)
                for j in range(self.dim0):
                    var s = tmp.select(nok,ok)
                    self.store[nelts](j,k,l,s)

            vectorize[sd, nelts](self.dim1)
        parallelize[calc_page](self.dim2,self.num_workers)



    
    fn cnpu(inout self) raises:
        if self.dim0 == 3:
            self.cnpu_3()
        elif self.dim0 == 4:
            self.cnpu_4()
        elif self.dim0 == 5:
            self.cnpu_5()
        elif self.dim0 == 6:
            self.cnpu_6()
        elif self.dim0 == 8:
            self.cnpu_8()
        elif self.dim0 == 10:
            self.cnpu_10()
        else:
            assert_true(False, "unsupported CNPU size")

    fn cnpu_3(inout self) raises:        
        assert_true(self.dim0 == 3,"Calling cnpu_3 on a Tensor3D with incompatible shape")     
        
        @parameter
        fn calc_page(l:Int):
            @parameter
            fn cnpu_3_loc[nelts: Int](k: Int):
                var r0 =  self.load[nelts](0, k,l)    
                var r1 =  self.load[nelts](1, k,l) 
                var r2 =  self.load[nelts](2, k,l)

                # get the first row of the result for the currently fetched SIMD vector
                var x = r1
                var y = r2
                var v = pe[nelts](x,y)        
                self.store[nelts](0,k,l,v) 

                # get the 2nd row of the result for the currently fetched SIMD vector
                x = r0
                y = r2
                v = pe[nelts](x,y)
                self.store[nelts](1,k,l,v)

                # get the 3rd row of the result for the currently fetched SIMD vector
                x = r0
                y = r1
                v = pe[nelts](x,y)
                self.store[nelts](2,k,l,v)

            # vectorize
            vectorize[cnpu_3_loc, nelts](self.dim1)
        parallelize[calc_page](self.dim2,self.num_workers)

    fn cnpu_4(inout self) raises:
        assert_true(self.dim0 == 4,"Calling cnpu_4 on a matrix with incompatible shape")
        @parameter
        fn calc_page(l:Int):
            @parameter
            fn cnpu_4_loc[nelts: Int](k: Int):
                var x_4_1 = self.load[nelts](0, k, l)
                var x_4_2 = self.load[nelts](1, k, l)
                var x_4_3 = self.load[nelts](2, k, l)
                var x_4_4 = self.load[nelts](3, k, l)
                var x_duo_1 = x_4_1
                var y_duo_1 = x_4_2
                var pc_4_1_to_2 = pe[nelts](x_duo_1,y_duo_1)
                var x_duo_2 = pc_4_1_to_2
                var y_duo_2 = x_4_3
                var pc_4_1_to_3 = pe[nelts](x_duo_2,y_duo_2)
                var x_duo_rev_4 = x_4_4
                var y_duo_rev_4 = x_4_3
                var pc_4_3_to_4 = pe[nelts](x_duo_rev_4,y_duo_rev_4)
                var x_duo_rev_3 = pc_4_3_to_4
                var y_duo_rev_3 = x_4_2
                var pc_4_2_to_4 = pe[nelts](x_duo_rev_3,y_duo_rev_3)
                var x_op_duo_2 = x_4_1
                var y_op_duo_2 = pc_4_3_to_4
                var x_op_duo_3 = pc_4_1_to_2
                var y_op_duo_3 = x_4_4
                var op_4_1 = pc_4_2_to_4
                var op_4_2 = pe[nelts](x_op_duo_2,y_op_duo_2)
                var op_4_3 = pe[nelts](x_op_duo_3,y_op_duo_3)
                var op_4_4 = pc_4_1_to_3
                self.store[nelts](0,k,l,op_4_1)
                self.store[nelts](1,k,l,op_4_2)
                self.store[nelts](2,k,l,op_4_3)
                self.store[nelts](3,k,l,op_4_4)
            # vectorize
            vectorize[cnpu_4_loc, nelts](self.dim1)
        parallelize[calc_page](self.dim2,self.num_workers)

    fn cnpu_5(inout self) raises:
        assert_true(self.dim0 == 5,"Calling cnpu_5 on a matrix with incompatible shape")
        @parameter
        fn calc_page(l:Int):
            @parameter
            fn cnpu_5_loc[nelts: Int](k: Int):
                var x_5_1 = self.load[nelts](0, k, l)
                var x_5_2 = self.load[nelts](1, k, l)
                var x_5_3 = self.load[nelts](2, k, l)
                var x_5_4 = self.load[nelts](3, k, l)
                var x_5_5 = self.load[nelts](4, k, l)
                var x_duo_1 = x_5_1
                var y_duo_1 = x_5_2
                var pc_5_1_to_2 = pe[nelts](x_duo_1,y_duo_1)
                var x_duo_2 = pc_5_1_to_2
                var y_duo_2 = x_5_3
                var pc_5_1_to_3 = pe[nelts](x_duo_2,y_duo_2)
                var x_duo_3 = pc_5_1_to_3
                var y_duo_3 = x_5_4
                var pc_5_1_to_4 = pe[nelts](x_duo_3,y_duo_3)
                var x_duo_rev_5 = x_5_5
                var y_duo_rev_5 = x_5_4
                var pc_5_4_to_5 = pe[nelts](x_duo_rev_5,y_duo_rev_5)
                var x_duo_rev_4 = pc_5_4_to_5
                var y_duo_rev_4 = x_5_3
                var pc_5_3_to_5 = pe[nelts](x_duo_rev_4,y_duo_rev_4)
                var x_duo_rev_3 = pc_5_3_to_5
                var y_duo_rev_3 = x_5_2
                var pc_5_2_to_5 = pe[nelts](x_duo_rev_3,y_duo_rev_3)
                var x_op_duo_2 = x_5_1
                var y_op_duo_2 = pc_5_3_to_5
                var x_op_duo_3 = pc_5_1_to_2
                var y_op_duo_3 = pc_5_4_to_5
                var x_op_duo_4 = pc_5_1_to_3
                var y_op_duo_4 = x_5_5
                var op_5_1 = pc_5_2_to_5
                var op_5_2 = pe[nelts](x_op_duo_2,y_op_duo_2)
                var op_5_3 = pe[nelts](x_op_duo_3,y_op_duo_3)
                var op_5_4 = pe[nelts](x_op_duo_4,y_op_duo_4)
                var op_5_5 = pc_5_1_to_4
                self.store[nelts](0,k,l,op_5_1)
                self.store[nelts](1,k,l,op_5_2)
                self.store[nelts](2,k,l,op_5_3)
                self.store[nelts](3,k,l,op_5_4)
                self.store[nelts](4,k,l,op_5_5)
            # vectorize
            vectorize[cnpu_5_loc, nelts](self.dim1)
        parallelize[calc_page](self.dim2,self.num_workers)

    fn cnpu_6(inout self) raises:
        assert_true(self.dim0 == 6,"Calling cnpu_6 on a matrix with incompatible shape")
        @parameter
        fn calc_page(l:Int):
            @parameter
            fn cnpu_6_loc[nelts: Int](k: Int):
                var x_6_1 = self.load[nelts](0, k, l)
                var x_6_2 = self.load[nelts](1, k, l)
                var x_6_3 = self.load[nelts](2, k, l)
                var x_6_4 = self.load[nelts](3, k, l)
                var x_6_5 = self.load[nelts](4, k, l)
                var x_6_6 = self.load[nelts](5, k, l)
                var x_duo_1 = x_6_1
                var y_duo_1 = x_6_2
                var pc_6_1_to_2 = pe[nelts](x_duo_1,y_duo_1)
                var x_duo_2 = pc_6_1_to_2
                var y_duo_2 = x_6_3
                var pc_6_1_to_3 = pe[nelts](x_duo_2,y_duo_2)
                var x_duo_3 = pc_6_1_to_3
                var y_duo_3 = x_6_4
                var pc_6_1_to_4 = pe[nelts](x_duo_3,y_duo_3)
                var x_duo_4 = pc_6_1_to_4
                var y_duo_4 = x_6_5
                var pc_6_1_to_5 = pe[nelts](x_duo_4,y_duo_4)
                var x_duo_rev_6 = x_6_6
                var y_duo_rev_6 = x_6_5
                var pc_6_5_to_6 = pe[nelts](x_duo_rev_6,y_duo_rev_6)
                var x_duo_rev_5 = pc_6_5_to_6
                var y_duo_rev_5 = x_6_4
                var pc_6_4_to_6 = pe[nelts](x_duo_rev_5,y_duo_rev_5)
                var x_duo_rev_4 = pc_6_4_to_6
                var y_duo_rev_4 = x_6_3
                var pc_6_3_to_6 = pe[nelts](x_duo_rev_4,y_duo_rev_4)
                var x_duo_rev_3 = pc_6_3_to_6
                var y_duo_rev_3 = x_6_2
                var pc_6_2_to_6 = pe[nelts](x_duo_rev_3,y_duo_rev_3)
                var x_op_duo_2 = x_6_1
                var y_op_duo_2 = pc_6_3_to_6
                var x_op_duo_3 = pc_6_1_to_2
                var y_op_duo_3 = pc_6_4_to_6
                var x_op_duo_4 = pc_6_1_to_3
                var y_op_duo_4 = pc_6_5_to_6
                var x_op_duo_5 = pc_6_1_to_4
                var y_op_duo_5 = x_6_6
                var op_6_1 = pc_6_2_to_6
                var op_6_2 = pe[nelts](x_op_duo_2,y_op_duo_2)
                var op_6_3 = pe[nelts](x_op_duo_3,y_op_duo_3)
                var op_6_4 = pe[nelts](x_op_duo_4,y_op_duo_4)
                var op_6_5 = pe[nelts](x_op_duo_5,y_op_duo_5)
                var op_6_6 = pc_6_1_to_5
                self.store[nelts](0,k,l,op_6_1)
                self.store[nelts](1,k,l,op_6_2)
                self.store[nelts](2,k,l,op_6_3)
                self.store[nelts](3,k,l,op_6_4)
                self.store[nelts](4,k,l,op_6_5)
                self.store[nelts](5,k,l,op_6_6)
            # vectorize
            vectorize[cnpu_6_loc, nelts](self.dim1)
        parallelize[calc_page](self.dim2,self.num_workers)

    

    fn cnpu_8(inout self) raises:
        assert_true(self.dim0 == 8,"Calling cnpu_8 on a matrix with incompatible shape")
        @parameter
        fn calc_page(l:Int):
            @parameter
            fn cnpu_8_loc[nelts: Int](k: Int):
                var x_8_1 = self.load[nelts](0, k, l)
                var x_8_2 = self.load[nelts](1, k, l)
                var x_8_3 = self.load[nelts](2, k, l)
                var x_8_4 = self.load[nelts](3, k, l)
                var x_8_5 = self.load[nelts](4, k, l)
                var x_8_6 = self.load[nelts](5, k, l)
                var x_8_7 = self.load[nelts](6, k, l)
                var x_8_8 = self.load[nelts](7, k, l)
                var x_duo_1 = x_8_1
                var y_duo_1 = x_8_2
                var pc_8_1_to_2 = pe[nelts](x_duo_1,y_duo_1)
                var x_duo_2 = pc_8_1_to_2
                var y_duo_2 = x_8_3
                var pc_8_1_to_3 = pe[nelts](x_duo_2,y_duo_2)
                var x_duo_3 = pc_8_1_to_3
                var y_duo_3 = x_8_4
                var pc_8_1_to_4 = pe[nelts](x_duo_3,y_duo_3)
                var x_duo_4 = pc_8_1_to_4
                var y_duo_4 = x_8_5
                var pc_8_1_to_5 = pe[nelts](x_duo_4,y_duo_4)
                var x_duo_5 = pc_8_1_to_5
                var y_duo_5 = x_8_6
                var pc_8_1_to_6 = pe[nelts](x_duo_5,y_duo_5)
                var x_duo_6 = pc_8_1_to_6
                var y_duo_6 = x_8_7
                var pc_8_1_to_7 = pe[nelts](x_duo_6,y_duo_6)
                var x_duo_rev_8 = x_8_8
                var y_duo_rev_8 = x_8_7
                var pc_8_7_to_8 = pe[nelts](x_duo_rev_8,y_duo_rev_8)
                var x_duo_rev_7 = pc_8_7_to_8
                var y_duo_rev_7 = x_8_6
                var pc_8_6_to_8 = pe[nelts](x_duo_rev_7,y_duo_rev_7)
                var x_duo_rev_6 = pc_8_6_to_8
                var y_duo_rev_6 = x_8_5
                var pc_8_5_to_8 = pe[nelts](x_duo_rev_6,y_duo_rev_6)
                var x_duo_rev_5 = pc_8_5_to_8
                var y_duo_rev_5 = x_8_4
                var pc_8_4_to_8 = pe[nelts](x_duo_rev_5,y_duo_rev_5)
                var x_duo_rev_4 = pc_8_4_to_8
                var y_duo_rev_4 = x_8_3
                var pc_8_3_to_8 = pe[nelts](x_duo_rev_4,y_duo_rev_4)
                var x_duo_rev_3 = pc_8_3_to_8
                var y_duo_rev_3 = x_8_2
                var pc_8_2_to_8 = pe[nelts](x_duo_rev_3,y_duo_rev_3)
                var x_op_duo_2 = x_8_1
                var y_op_duo_2 = pc_8_3_to_8
                var x_op_duo_3 = pc_8_1_to_2
                var y_op_duo_3 = pc_8_4_to_8
                var x_op_duo_4 = pc_8_1_to_3
                var y_op_duo_4 = pc_8_5_to_8
                var x_op_duo_5 = pc_8_1_to_4
                var y_op_duo_5 = pc_8_6_to_8
                var x_op_duo_6 = pc_8_1_to_5
                var y_op_duo_6 = pc_8_7_to_8
                var x_op_duo_7 = pc_8_1_to_6
                var y_op_duo_7 = x_8_8
                var op_8_1 = pc_8_2_to_8
                var op_8_2 = pe[nelts](x_op_duo_2,y_op_duo_2)
                var op_8_3 = pe[nelts](x_op_duo_3,y_op_duo_3)
                var op_8_4 = pe[nelts](x_op_duo_4,y_op_duo_4)
                var op_8_5 = pe[nelts](x_op_duo_5,y_op_duo_5)
                var op_8_6 = pe[nelts](x_op_duo_6,y_op_duo_6)
                var op_8_7 = pe[nelts](x_op_duo_7,y_op_duo_7)
                var op_8_8 = pc_8_1_to_7
                self.store[nelts](0,k,l,op_8_1)
                self.store[nelts](1,k,l,op_8_2)
                self.store[nelts](2,k,l,op_8_3)
                self.store[nelts](3,k,l,op_8_4)
                self.store[nelts](4,k,l,op_8_5)
                self.store[nelts](5,k,l,op_8_6)
                self.store[nelts](6,k,l,op_8_7)
                self.store[nelts](7,k,l,op_8_8)
            # vectorize
            vectorize[cnpu_8_loc, nelts](self.dim1)
        parallelize[calc_page](self.dim2,self.num_workers)

  

    fn cnpu_10(inout self) raises:
        assert_true(self.dim0 == 10,"Calling cnpu_10 on a matrix with incompatible shape")
        @parameter
        fn calc_page(l:Int):
            @parameter
            fn cnpu_10_loc[nelts: Int](k: Int):
                var x_10_1 = self.load[nelts](0, k, l)
                var x_10_2 = self.load[nelts](1, k, l)
                var x_10_3 = self.load[nelts](2, k, l)
                var x_10_4 = self.load[nelts](3, k, l)
                var x_10_5 = self.load[nelts](4, k, l)
                var x_10_6 = self.load[nelts](5, k, l)
                var x_10_7 = self.load[nelts](6, k, l)
                var x_10_8 = self.load[nelts](7, k, l)
                var x_10_9 = self.load[nelts](8, k, l)
                var x_10_10 = self.load[nelts](9, k, l)
                var x_duo_1 = x_10_1
                var y_duo_1 = x_10_2
                var pc_10_1_to_2 = pe[nelts](x_duo_1,y_duo_1)
                var x_duo_2 = pc_10_1_to_2
                var y_duo_2 = x_10_3
                var pc_10_1_to_3 = pe[nelts](x_duo_2,y_duo_2)
                var x_duo_3 = pc_10_1_to_3
                var y_duo_3 = x_10_4
                var pc_10_1_to_4 = pe[nelts](x_duo_3,y_duo_3)
                var x_duo_4 = pc_10_1_to_4
                var y_duo_4 = x_10_5
                var pc_10_1_to_5 = pe[nelts](x_duo_4,y_duo_4)
                var x_duo_5 = pc_10_1_to_5
                var y_duo_5 = x_10_6
                var pc_10_1_to_6 = pe[nelts](x_duo_5,y_duo_5)
                var x_duo_6 = pc_10_1_to_6
                var y_duo_6 = x_10_7
                var pc_10_1_to_7 = pe[nelts](x_duo_6,y_duo_6)
                var x_duo_7 = pc_10_1_to_7
                var y_duo_7 = x_10_8
                var pc_10_1_to_8 = pe[nelts](x_duo_7,y_duo_7)
                var x_duo_8 = pc_10_1_to_8
                var y_duo_8 = x_10_9
                var pc_10_1_to_9 = pe[nelts](x_duo_8,y_duo_8)
                var x_duo_rev_10 = x_10_10
                var y_duo_rev_10 = x_10_9
                var pc_10_9_to_10 = pe[nelts](x_duo_rev_10,y_duo_rev_10)
                var x_duo_rev_9 = pc_10_9_to_10
                var y_duo_rev_9 = x_10_8
                var pc_10_8_to_10 = pe[nelts](x_duo_rev_9,y_duo_rev_9)
                var x_duo_rev_8 = pc_10_8_to_10
                var y_duo_rev_8 = x_10_7
                var pc_10_7_to_10 = pe[nelts](x_duo_rev_8,y_duo_rev_8)
                var x_duo_rev_7 = pc_10_7_to_10
                var y_duo_rev_7 = x_10_6
                var pc_10_6_to_10 = pe[nelts](x_duo_rev_7,y_duo_rev_7)
                var x_duo_rev_6 = pc_10_6_to_10
                var y_duo_rev_6 = x_10_5
                var pc_10_5_to_10 = pe[nelts](x_duo_rev_6,y_duo_rev_6)
                var x_duo_rev_5 = pc_10_5_to_10
                var y_duo_rev_5 = x_10_4
                var pc_10_4_to_10 = pe[nelts](x_duo_rev_5,y_duo_rev_5)
                var x_duo_rev_4 = pc_10_4_to_10
                var y_duo_rev_4 = x_10_3
                var pc_10_3_to_10 = pe[nelts](x_duo_rev_4,y_duo_rev_4)
                var x_duo_rev_3 = pc_10_3_to_10
                var y_duo_rev_3 = x_10_2
                var pc_10_2_to_10 = pe[nelts](x_duo_rev_3,y_duo_rev_3)
                var x_op_duo_2 = x_10_1
                var y_op_duo_2 = pc_10_3_to_10
                var x_op_duo_3 = pc_10_1_to_2
                var y_op_duo_3 = pc_10_4_to_10
                var x_op_duo_4 = pc_10_1_to_3
                var y_op_duo_4 = pc_10_5_to_10
                var x_op_duo_5 = pc_10_1_to_4
                var y_op_duo_5 = pc_10_6_to_10
                var x_op_duo_6 = pc_10_1_to_5
                var y_op_duo_6 = pc_10_7_to_10
                var x_op_duo_7 = pc_10_1_to_6
                var y_op_duo_7 = pc_10_8_to_10
                var x_op_duo_8 = pc_10_1_to_7
                var y_op_duo_8 = pc_10_9_to_10
                var x_op_duo_9 = pc_10_1_to_8
                var y_op_duo_9 = x_10_10
                var op_10_1 = pc_10_2_to_10
                var op_10_2 = pe[nelts](x_op_duo_2,y_op_duo_2)
                var op_10_3 = pe[nelts](x_op_duo_3,y_op_duo_3)
                var op_10_4 = pe[nelts](x_op_duo_4,y_op_duo_4)
                var op_10_5 = pe[nelts](x_op_duo_5,y_op_duo_5)
                var op_10_6 = pe[nelts](x_op_duo_6,y_op_duo_6)
                var op_10_7 = pe[nelts](x_op_duo_7,y_op_duo_7)
                var op_10_8 = pe[nelts](x_op_duo_8,y_op_duo_8)
                var op_10_9 = pe[nelts](x_op_duo_9,y_op_duo_9)
                var op_10_10 = pc_10_1_to_9
                self.store[nelts](0,k,l,op_10_1)
                self.store[nelts](1,k,l,op_10_2)
                self.store[nelts](2,k,l,op_10_3)
                self.store[nelts](3,k,l,op_10_4)
                self.store[nelts](4,k,l,op_10_5)
                self.store[nelts](5,k,l,op_10_6)
                self.store[nelts](6,k,l,op_10_7)
                self.store[nelts](7,k,l,op_10_8)
                self.store[nelts](8,k,l,op_10_9)
                self.store[nelts](9,k,l,op_10_10)
            # vectorize
            vectorize[cnpu_10_loc, nelts](self.dim1)
        parallelize[calc_page](self.dim2,self.num_workers)

    





   




    


--- ldpcdec.mojo ---

from sys.info import simdwidthof
from codebook.luts import nc,nnz, ncnpu, nvnpu,  vnpu_sizes, vnpu_indices, cnpu_sizes, cnpu_indices
from ldpc.types import nelts,dtype,Tensor3D, parallel_decode
from tensor import Tensor, TensorSpec, TensorShape
from random import rand, seed
from sys.info import num_physical_cores, num_logical_cores, num_performance_cores

from python import Python
from python.object import PythonObject


from sys.info import (
    os_is_linux,
    os_is_windows,
    os_is_macos,
    has_sse4,
    has_avx,
    has_avx2,
    has_avx512f,
    has_vnni,
    has_neon,
    is_apple_m1,
    has_intel_amx,
    _current_target,
    _current_cpu,
    _triple_attr,
)


from time import now
def main():
    
    var os = ""
    if os_is_linux():
        os = "linux"
    elif os_is_macos():
        os = "macOS"
    else:
        os = "windows"
    var cpu = String(_current_cpu())
    var arch = String(_triple_attr())
    var cpu_features = String("")
    if has_sse4():
        cpu_features += " sse4"
    if has_avx():
        cpu_features += " avx"
    if has_avx2():
        cpu_features += " avx2"
    if has_avx512f():
        cpu_features += " avx512f"
    if has_vnni():
        if has_avx512f():
            cpu_features += " avx512_vnni"
        else:
            cpu_features += " avx_vnni"
    if has_intel_amx():
        cpu_features += " intel_amx"
    if has_neon():
        cpu_features += " neon"
    if is_apple_m1():
        cpu_features += " Apple M1"

    print("System information: ")
    print("    OS          : ", os)
    print("    CPU         : ", cpu)
    print("    Arch        : ", arch)
    print("    Num Cores   : ", num_logical_cores())
    print("    CPU Features:", cpu_features)
    
    print("SIMD width of ",dtype, " = ",nelts)
   
    alias base_niter = 10
    alias max_niter = 20
    var nvcpu = num_logical_cores()

    # between following two we must balance the available nvcpu
    var intra_codeword_parallellism_factor = 1
    var nthread = nvcpu//2 +2

    alias ncodewordperthread = 4*nelts # must be a multiple of simdwidth = nelts    

      


    # get binary heavy LUTs
    var rep_spec = TensorSpec(DType.uint16, nc, 1)
    var rep_indices_tensor = Tensor[DType.uint16](rep_spec)
    rep_indices_tensor = rep_indices_tensor.fromfile('codebook/repeats.bin')

    var rep_offsets_tensor = Tensor[DType.uint16](rep_spec)
    rep_offsets_tensor = rep_offsets_tensor.fromfile('codebook/repeats_offsets.bin')

    var c2r_spec = TensorSpec(DType.uint16, nnz, 1)
    var c2r_indices_tensor = Tensor[DType.uint16](c2r_spec)
    c2r_indices_tensor = c2r_indices_tensor.fromfile('codebook/c2r.bin')

    var r2c_indices_tensor = Tensor[DType.uint16](c2r_spec)
    r2c_indices_tensor = r2c_indices_tensor.fromfile('codebook/r2c.bin')



    # var batch_size: Int
    var X: Tensor3D  # columns are codewords

    # warmup
    print("warmup...")
    var batch_syndrome: Int = -1
    var batch_size: Int

    batch_size = ncodewordperthread*nthread
    print("batch_size: ", batch_size)
    X = Tensor3D(nc,batch_size)
    X.rand_init()
 

    print("ncodewordperthread: ", ncodewordperthread)
    print("nthread: ",nthread)

    # prepare an output buffer for the decoded batch (warning: now rows are codewords)
    var Y = Tensor3D(batch_size, nc)   
     
    X.print()

    parallel_decode[base_niter, max_niter](X, Y, batch_syndrome,intra_codeword_parallellism_factor, nthread, nc,nnz, ncnpu, nvnpu, ncodewordperthread, rep_indices_tensor, rep_offsets_tensor,c2r_indices_tensor, r2c_indices_tensor, vnpu_sizes, vnpu_indices, cnpu_sizes, cnpu_indices)
    print("Y after decoding")
    Y.print[3,3]()
    print("warmup Batch syndrome: ",batch_syndrome)


    # # now benchmark
    print("now timeit: ...")
    var ncall = 10
    var tot_msecs = 0.0
    for i in range(ncall):
        # avoid that the compiler thinks there is no sense decoding same input X time and time again
        X.rand_init()

        var start = now()           
        parallel_decode[base_niter, max_niter](X, Y, batch_syndrome,intra_codeword_parallellism_factor, nthread, nc,nnz, ncnpu, nvnpu, ncodewordperthread, rep_indices_tensor, rep_offsets_tensor,c2r_indices_tensor, r2c_indices_tensor, vnpu_sizes, vnpu_indices, cnpu_sizes, cnpu_indices)
        var dt = (1e-6*(now()-start))
        tot_msecs += dt
    var msecs = tot_msecs/ncall

    
    
    var mbps: Float32 = 1e-3*batch_size * nc/msecs
    print("Throughput is ", batch_size," codewords in ",msecs, "msec, equivalent to ",mbps," Mbps")
    








--- .gitignore ---

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
#   For a library or package, you might want to ignore these files since the code is
#   intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# poetry
#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
#   This is especially recommended for binary packages to ensure reproducibility, and is more
#   commonly ignored for libraries.
#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock

# pdm
#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
#   in version control.
#   https://pdm.fming.dev/#use-with-ide
.pdm.toml

# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

# PyCharm
#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
#  and can be added to the global gitignore or merged into this file.  For a more nuclear
#  option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/


--- LICENSE ---

MIT License

Copyright (c) 2023 Maxim Zaks

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


--- README.md ---

# mojo-hash
A collection of hash functions implemented in Mojo.

## AHash
Original repo: https://github.com/tkaitchuck/aHash 
Note: implements the fallback version (without AES-NI intrinsics use), uses folded multiply function without u128 support

## fnv1a
Original repo: https://github.com/ziglang/zig/blob/master/lib/std/hash/fnv.zig
Note: implements 32 and 64 bit variants

## fxhash
Original repo: https://github.com/cbreeden/fxhash/tree/master
Note: implements 32 and 64 bit variants

## Wyhash
Original repo: https://github.com/wangyi-fudan/wyhash
Note: `wymum` implemented as if `WYHASH_32BIT_MUM` is set and `WYHASH_CONDOM` not set. Little endian only.

## Benachmark
Collecets average hash function runtime in nanoseconds based on 7 different word collections. The average runtime is computed 20 times on each word collection, the fastest is kept as final result. Shows collision on full 32/64 bit space and 1024 mod (10 bit) space

### Results

CPU Specs: 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz

```
Corpus 1
Word count 100 | unique word count 82 | min key size 2 | avg key size 5.71 | max key size 12
AHash avg hash compute 18.149999999999999 | hash colision 1.0 | hash colision mod 512 1.1549295774647887
Wyhash avg hash compute 17.079999999999998 | hash colision 1.0 | hash colision mod 512 1.1232876712328768
fnv1a32 avg hash compute 15.08 | hash colision 1.0 | hash colision mod 512 1.1232876712328768
fnv1a64 avg hash compute 16.32 | hash colision 1.0 | hash colision mod 512 1.0249999999999999
fxHash32 avg hash compute 12.539999999999999 | hash colision 1.0 | hash colision mod 512 1.2238805970149254
fxHash64 avg hash compute 12.56 | hash colision 1.0 | hash colision mod 512 1.1884057971014492
std_Hash64 avg hash compute 213.0 | hash colision 1.0 | hash colision mod 512 1.0512820512820513

Corpus 2
Word count 999 | unique word count 203 | min key size 1 | avg key size 4.8058058058058055 | max key size 14
AHash avg hash compute 18.263263263263262 | hash colision 1.0 | hash colision mod 512 1.2083333333333333
Wyhash avg hash compute 20.11011011011011 | hash colision 1.0 | hash colision mod 512 1.2303030303030302
fnv1a32 avg hash compute 17.995995995995997 | hash colision 1.0 | hash colision mod 512 1.2848101265822784
fnv1a64 avg hash compute 16.079079079079079 | hash colision 1.0 | hash colision mod 512 1.2011834319526626
fxHash32 avg hash compute 14.397397397397397 | hash colision 1.0 | hash colision mod 512 1.3716216216216217
fxHash64 avg hash compute 12.603603603603604 | hash colision 1.0 | hash colision mod 512 1.4195804195804196
std_Hash64 avg hash compute 239.15815815815816 | hash colision 1.0 | hash colision mod 512 1.2303030303030302

Corpus 3
Word count 999 | unique word count 192 | min key size 1 | avg key size 4.293293293293293 | max key size 13
AHash avg hash compute 16.716716716716718 | hash colision 1.0 | hash colision mod 512 1.1636363636363636
Wyhash avg hash compute 16.952952952952952 | hash colision 1.0 | hash colision mod 512 1.2151898734177216
fnv1a32 avg hash compute 15.968968968968969 | hash colision 1.0 | hash colision mod 512 1.1428571428571428
fnv1a64 avg hash compute 18.862862862862862 | hash colision 1.0 | hash colision mod 512 1.2229299363057324
fxHash32 avg hash compute 15.723723723723724 | hash colision 1.0 | hash colision mod 512 1.352112676056338
fxHash64 avg hash compute 17.168168168168169 | hash colision 1.0 | hash colision mod 512 1.4436090225563909
std_Hash64 avg hash compute 258.6146146146146 | hash colision 1.0 | hash colision mod 512 1.1779141104294479

Corpus 4
Word count 999 | unique word count 532 | min key size 2 | avg key size 10.646646646646646 | max key size 37
AHash avg hash compute 20.205205205205207 | hash colision 1.0 | hash colision mod 512 1.5786350148367954
Wyhash avg hash compute 20.234234234234233 | hash colision 1.0 | hash colision mod 512 1.5975975975975976
fnv1a32 avg hash compute 21.814814814814813 | hash colision 1.0 | hash colision mod 512 1.6170212765957446
fnv1a64 avg hash compute 24.41041041041041 | hash colision 1.0 | hash colision mod 512 1.5928143712574849
fxHash32 avg hash compute 16.208208208208209 | hash colision 1.0 | hash colision mod 512 1.6677115987460815
fxHash64 avg hash compute 15.890890890890891 | hash colision 1.0 | hash colision mod 512 1.9850746268656716
std_Hash64 avg hash compute 218.3093093093093 | hash colision 1.0018832391713748 | hash colision mod 512 1.6170212765957446

Corpus 5
Word count 999 | unique word count 208 | min key size 2 | avg key size 5.6496496496496498 | max key size 18
AHash avg hash compute 15.921921921921921 | hash colision 1.0 | hash colision mod 512 1.1620111731843576
Wyhash avg hash compute 19.517517517517518 | hash colision 1.0 | hash colision mod 512 1.1685393258426966
fnv1a32 avg hash compute 17.042042042042041 | hash colision 1.0 | hash colision mod 512 1.2093023255813953
fnv1a64 avg hash compute 18.58958958958959 | hash colision 1.0 | hash colision mod 512 1.2530120481927711
fxHash32 avg hash compute 14.552552552552553 | hash colision 1.0 | hash colision mod 512 1.3506493506493507
fxHash64 avg hash compute 14.527527527527528 | hash colision 1.0 | hash colision mod 512 1.3594771241830066
std_Hash64 avg hash compute 239.1181181181181 | hash colision 1.0 | hash colision mod 512 1.2023121387283238

Corpus 6
Word count 10 | unique word count 10 | min key size 378 | avg key size 499.19999999999999 | max key size 558
AHash avg hash compute 67.400000000000006 | hash colision 1.0 | hash colision mod 512 1.0
Wyhash avg hash compute 64.200000000000003 | hash colision 1.0 | hash colision mod 512 1.0
fnv1a32 avg hash compute 499.60000000000002 | hash colision 1.0 | hash colision mod 512 1.0
fnv1a64 avg hash compute 620.70000000000005 | hash colision 1.0 | hash colision mod 512 1.0
fxHash32 avg hash compute 163.80000000000001 | hash colision 1.0 | hash colision mod 512 1.0
fxHash64 avg hash compute 87.799999999999997 | hash colision 1.0 | hash colision mod 512 1.0
std_Hash64 avg hash compute 247.59999999999999 | hash colision 1.0 | hash colision mod 512 1.0

Corpus 7
Word count 161 | unique word count 143 | min key size 8 | avg key size 22.260869565217391 | max key size 43
AHash avg hash compute 19.546583850931675 | hash colision 1.0 | hash colision mod 512 1.1259842519685039
Wyhash avg hash compute 22.670807453416149 | hash colision 1.0 | hash colision mod 512 1.1439999999999999
fnv1a32 avg hash compute 32.900621118012424 | hash colision 1.0 | hash colision mod 512 1.153225806451613
fnv1a64 avg hash compute 38.391304347826086 | hash colision 1.0 | hash colision mod 512 1.1626016260162602
fxHash32 avg hash compute 20.043478260869566 | hash colision 1.0 | hash colision mod 512 1.1259842519685039
fxHash64 avg hash compute 19.503105590062113 | hash colision 1.0 | hash colision mod 512 1.153225806451613
std_Hash64 avg hash compute 242.59006211180125 | hash colision 1.0 | hash colision mod 512 1.1626016260162602
```

MacMini M1, 2020

```
Corpus 1
Word count 100 | unique word count 82 | min key size 2 | avg key size 5.71 | max key size 12
AHash avg hash compute 19.0 | hash colision 1.0 | hash colision mod 512 1.1549295774647887
Wyhash avg hash compute 29.5 | hash colision 1.0 | hash colision mod 512 1.1232876712328768
fnv1a32 avg hash compute 18.5 | hash colision 1.0 | hash colision mod 512 1.1232876712328768
fnv1a64 avg hash compute 17.5 | hash colision 1.0 | hash colision mod 512 1.0249999999999999
fxHash32 avg hash compute 18.0 | hash colision 1.0 | hash colision mod 512 1.2238805970149254
fxHash64 avg hash compute 19.5 | hash colision 1.0 | hash colision mod 512 1.1884057971014492
std_Hash64 avg hash compute 84.5 | hash colision 1.0 | hash colision mod 512 1.0512820512820513

Corpus 2
Word count 999 | unique word count 203 | min key size 1 | avg key size 4.8058058058058055 | max key size 14
AHash avg hash compute 17.567567567567568 | hash colision 1.0 | hash colision mod 512 1.2083333333333333
Wyhash avg hash compute 25.925925925925927 | hash colision 1.0 | hash colision mod 512 1.2303030303030302
fnv1a32 avg hash compute 19.96996996996997 | hash colision 1.0 | hash colision mod 512 1.2848101265822784
fnv1a64 avg hash compute 17.967967967967969 | hash colision 1.0 | hash colision mod 512 1.2011834319526626
fxHash32 avg hash compute 16.016016016016017 | hash colision 1.0 | hash colision mod 512 1.3716216216216217
fxHash64 avg hash compute 13.863863863863864 | hash colision 1.0 | hash colision mod 512 1.4195804195804196
std_Hash64 avg hash compute 75.17517517517517 | hash colision 1.0 | hash colision mod 512 1.2303030303030302

Corpus 3
Word count 999 | unique word count 192 | min key size 1 | avg key size 4.293293293293293 | max key size 13
AHash avg hash compute 18.468468468468469 | hash colision 1.0 | hash colision mod 512 1.1636363636363636
Wyhash avg hash compute 24.474474474474473 | hash colision 1.0 | hash colision mod 512 1.2151898734177216
fnv1a32 avg hash compute 19.81981981981982 | hash colision 1.0 | hash colision mod 512 1.1428571428571428
fnv1a64 avg hash compute 17.417417417417418 | hash colision 1.0 | hash colision mod 512 1.2229299363057324
fxHash32 avg hash compute 15.665665665665665 | hash colision 1.0 | hash colision mod 512 1.352112676056338
fxHash64 avg hash compute 16.216216216216218 | hash colision 1.0 | hash colision mod 512 1.4436090225563909
std_Hash64 avg hash compute 87.037037037037038 | hash colision 1.0 | hash colision mod 512 1.1779141104294479

Corpus 4
Word count 999 | unique word count 532 | min key size 2 | avg key size 10.646646646646646 | max key size 37
AHash avg hash compute 19.51951951951952 | hash colision 1.0 | hash colision mod 512 1.5786350148367954
Wyhash avg hash compute 24.874874874874873 | hash colision 1.0 | hash colision mod 512 1.5975975975975976
fnv1a32 avg hash compute 25.575575575575577 | hash colision 1.0 | hash colision mod 512 1.6170212765957446
fnv1a64 avg hash compute 24.274274274274273 | hash colision 1.0 | hash colision mod 512 1.5928143712574849
fxHash32 avg hash compute 15.665665665665665 | hash colision 1.0 | hash colision mod 512 1.6677115987460815
fxHash64 avg hash compute 17.867867867867869 | hash colision 1.0 | hash colision mod 512 1.9850746268656716
std_Hash64 avg hash compute 73.523523523523522 | hash colision 1.0018832391713748 | hash colision mod 512 1.5833333333333333

Corpus 5
Word count 999 | unique word count 208 | min key size 2 | avg key size 5.6496496496496498 | max key size 18
AHash avg hash compute 17.817817817817819 | hash colision 1.0 | hash colision mod 512 1.1620111731843576
Wyhash avg hash compute 26.576576576576578 | hash colision 1.0 | hash colision mod 512 1.1685393258426966
fnv1a32 avg hash compute 19.76976976976977 | hash colision 1.0 | hash colision mod 512 1.2093023255813953
fnv1a64 avg hash compute 18.918918918918919 | hash colision 1.0 | hash colision mod 512 1.2530120481927711
fxHash32 avg hash compute 17.817817817817819 | hash colision 1.0 | hash colision mod 512 1.3506493506493507
fxHash64 avg hash compute 15.715715715715715 | hash colision 1.0 | hash colision mod 512 1.3594771241830066
std_Hash64 avg hash compute 75.425425425425431 | hash colision 1.0 | hash colision mod 512 1.2023121387283238

Corpus 6
Word count 10 | unique word count 10 | min key size 378 | avg key size 499.19999999999999 | max key size 558
AHash avg hash compute 30.0 | hash colision 1.0 | hash colision mod 512 1.0
Wyhash avg hash compute 90.0 | hash colision 1.0 | hash colision mod 512 1.0
fnv1a32 avg hash compute 635.0 | hash colision 1.0 | hash colision mod 512 1.0
fnv1a64 avg hash compute 660.0 | hash colision 1.0 | hash colision mod 512 1.0
fxHash32 avg hash compute 250.0 | hash colision 1.0 | hash colision mod 512 1.0
fxHash64 avg hash compute 145.0 | hash colision 1.0 | hash colision mod 512 1.0
std_Hash64 avg hash compute 220.0 | hash colision 1.0 | hash colision mod 512 1.0

Corpus 7
Word count 161 | unique word count 143 | min key size 8 | avg key size 22.260869565217391 | max key size 43
AHash avg hash compute 17.701863354037268 | hash colision 1.0 | hash colision mod 512 1.1259842519685039
Wyhash avg hash compute 29.19254658385093 | hash colision 1.0 | hash colision mod 512 1.1439999999999999
fnv1a32 avg hash compute 42.546583850931675 | hash colision 1.0 | hash colision mod 512 1.153225806451613
fnv1a64 avg hash compute 39.440993788819874 | hash colision 1.0 | hash colision mod 512 1.1626016260162602
fxHash32 avg hash compute 18.012422360248447 | hash colision 1.0 | hash colision mod 512 1.1259842519685039
fxHash64 avg hash compute 19.875776397515526 | hash colision 1.0 | hash colision mod 512 1.153225806451613
std_Hash64 avg hash compute 113.35403726708074 | hash colision 1.0 | hash colision mod 512 1.1259842519685039
```

![Hash functions benchmark chart](images/hash_functions.png)

### Other languages benchmarks results:

#### CPU Specs: 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz

**Rust**

```
Avg time Default, 16.622: total elements: 100, unique elements: 82, collisions: 1.0, collisions % 512: 1.0649350649350648, keys min: 2, avg: 6, max: 12
Avg time FxHasher, 17.769000000000002: total elements: 100, unique elements: 82, collisions: 1.0, collisions % 512: 1.0933333333333333, keys min: 2, avg: 6, max: 12
Avg time AHasher, 16.5035: total elements: 100, unique elements: 82, collisions: 1.0, collisions % 512: 1.1232876712328768, keys min: 2, avg: 6, max: 12
Avg time WyHash, 17.2495: total elements: 100, unique elements: 82, collisions: 1.0, collisions % 512: 1.0933333333333333, keys min: 2, avg: 6, max: 12
Avg time Default, 16.026176176176175: total elements: 999, unique elements: 203, collisions: 1.0, collisions % 512: 1.1871345029239766, keys min: 1, avg: 5, max: 14
Avg time FxHasher, 16.14034034034034: total elements: 999, unique elements: 203, collisions: 1.0, collisions % 512: 1.2083333333333333, keys min: 1, avg: 5, max: 14
Avg time AHasher, 16.455305305305306: total elements: 999, unique elements: 203, collisions: 1.0, collisions % 512: 1.2011834319526626, keys min: 1, avg: 5, max: 14
Avg time WyHash, 17.575925925925926: total elements: 999, unique elements: 203, collisions: 1.0, collisions % 512: 1.2083333333333333, keys min: 1, avg: 5, max: 14
Avg time Default, 16.30725725725726: total elements: 999, unique elements: 192, collisions: 1.0, collisions % 512: 1.238709677419355, keys min: 1, avg: 5, max: 13
Avg time FxHasher, 17.076676676676676: total elements: 999, unique elements: 192, collisions: 1.0, collisions % 512: 1.263157894736842, keys min: 1, avg: 5, max: 13
Avg time AHasher, 16.477227227227228: total elements: 999, unique elements: 192, collisions: 1.0, collisions % 512: 1.2229299363057324, keys min: 1, avg: 5, max: 13
Avg time WyHash, 17.61991991991992: total elements: 999, unique elements: 192, collisions: 1.0, collisions % 512: 1.2075471698113207, keys min: 1, avg: 5, max: 13
Avg time Default, 17.10630630630631: total elements: 999, unique elements: 532, collisions: 1.0, collisions % 512: 1.5647058823529412, keys min: 2, avg: 12, max: 37
Avg time FxHasher, 18.07007007007007: total elements: 999, unique elements: 532, collisions: 1.0, collisions % 512: 1.6419753086419753, keys min: 2, avg: 12, max: 37
Avg time AHasher, 17.31981981981982: total elements: 999, unique elements: 532, collisions: 1.0, collisions % 512: 1.636923076923077, keys min: 2, avg: 12, max: 37
Avg time WyHash, 17.735835835835836: total elements: 999, unique elements: 532, collisions: 1.0, collisions % 512: 1.636923076923077, keys min: 2, avg: 12, max: 37
Avg time Default, 16.716816816816817: total elements: 999, unique elements: 208, collisions: 1.0, collisions % 512: 1.2606060606060605, keys min: 2, avg: 6, max: 18
Avg time FxHasher, 17.642342342342342: total elements: 999, unique elements: 208, collisions: 1.0, collisions % 512: 1.2606060606060605, keys min: 2, avg: 6, max: 18
Avg time AHasher, 16.40915915915916: total elements: 999, unique elements: 208, collisions: 1.0, collisions % 512: 1.1954022988505748, keys min: 2, avg: 6, max: 18
Avg time WyHash, 17.5506006006006: total elements: 999, unique elements: 208, collisions: 1.0, collisions % 512: 1.1751412429378532, keys min: 2, avg: 6, max: 18
Avg time Default, 126.03: total elements: 10, unique elements: 10, collisions: 1.0, collisions % 512: 1.0, keys min: 378, avg: 499, max: 558
Avg time FxHasher, 93.1: total elements: 10, unique elements: 10, collisions: 1.0, collisions % 512: 1.0, keys min: 378, avg: 499, max: 558
Avg time AHasher, 48.14: total elements: 10, unique elements: 10, collisions: 1.0, collisions % 512: 1.0, keys min: 378, avg: 499, max: 558
Avg time WyHash, 43.175: total elements: 10, unique elements: 10, collisions: 1.0, collisions % 512: 1.0, keys min: 378, avg: 499, max: 558
Avg time Default, 22.654658385093168: total elements: 161, unique elements: 143, collisions: 1.0, collisions % 512: 1.125984251968504, keys min: 8, avg: 22, max: 43
Avg time FxHasher, 20.537888198757763: total elements: 161, unique elements: 143, collisions: 1.0, collisions % 512: 1.1916666666666667, keys min: 8, avg: 22, max: 43
Avg time AHasher, 17.930124223602483: total elements: 161, unique elements: 143, collisions: 1.0, collisions % 512: 1.1349206349206349, keys min: 8, avg: 22, max: 43
Avg time WyHash, 19.022360248447203: total elements: 161, unique elements: 143, collisions: 1.0, collisions % 512: 1.1349206349206349, keys min: 8, avg: 22, max: 43
```

#### MacMini M1, 2020

**Rust**

```
Avg time Default, 26.552: total elements: 100, unique elements: 82, collisions: 1.0, collisions % 512: 1.0649350649350648, keys min: 2, avg: 6, max: 12
Avg time FxHasher, 25.7875: total elements: 100, unique elements: 82, collisions: 1.0, collisions % 512: 1.0933333333333333, keys min: 2, avg: 6, max: 12
Avg time AHasher, 26.688499999999998: total elements: 100, unique elements: 82, collisions: 1.0, collisions % 512: 1.1549295774647887, keys min: 2, avg: 6, max: 12
Avg time WyHash, 27.168499999999998: total elements: 100, unique elements: 82, collisions: 1.0, collisions % 512: 1.0933333333333333, keys min: 2, avg: 6, max: 12
Avg time Default, 30.68533533533534: total elements: 999, unique elements: 203, collisions: 1.0, collisions % 512: 1.1871345029239766, keys min: 1, avg: 5, max: 14
Avg time FxHasher, 32.62207207207207: total elements: 999, unique elements: 203, collisions: 1.0, collisions % 512: 1.2083333333333333, keys min: 1, avg: 5, max: 14
Avg time AHasher, 30.133333333333333: total elements: 999, unique elements: 203, collisions: 1.0, collisions % 512: 1.26875, keys min: 1, avg: 5, max: 14
Avg time WyHash, 30.666916916916918: total elements: 999, unique elements: 203, collisions: 1.0, collisions % 512: 1.2083333333333333, keys min: 1, avg: 5, max: 14
Avg time Default, 28.71331331331331: total elements: 999, unique elements: 192, collisions: 1.0, collisions % 512: 1.238709677419355, keys min: 1, avg: 5, max: 13
Avg time FxHasher, 25.97787787787788: total elements: 999, unique elements: 192, collisions: 1.0, collisions % 512: 1.263157894736842, keys min: 1, avg: 5, max: 13
Avg time AHasher, 26.035535535535537: total elements: 999, unique elements: 192, collisions: 1.0, collisions % 512: 1.238709677419355, keys min: 1, avg: 5, max: 13
Avg time WyHash, 26.91166166166166: total elements: 999, unique elements: 192, collisions: 1.0, collisions % 512: 1.2075471698113207, keys min: 1, avg: 5, max: 13
Avg time Default, 24.716066066066066: total elements: 999, unique elements: 532, collisions: 1.0, collisions % 512: 1.5647058823529412, keys min: 2, avg: 12, max: 37
Avg time FxHasher, 23.58993993993994: total elements: 999, unique elements: 532, collisions: 1.0, collisions % 512: 1.6419753086419753, keys min: 2, avg: 12, max: 37
Avg time AHasher, 23.47817817817818: total elements: 999, unique elements: 532, collisions: 1.0, collisions % 512: 1.6269113149847094, keys min: 2, avg: 12, max: 37
Avg time WyHash, 21.5007007007007: total elements: 999, unique elements: 532, collisions: 1.0, collisions % 512: 1.636923076923077, keys min: 2, avg: 12, max: 37
Avg time Default, 21.82362362362362: total elements: 999, unique elements: 208, collisions: 1.0, collisions % 512: 1.2606060606060605, keys min: 2, avg: 6, max: 18
Avg time FxHasher, 21.375575575575578: total elements: 999, unique elements: 208, collisions: 1.0, collisions % 512: 1.2606060606060605, keys min: 2, avg: 6, max: 18
Avg time AHasher, 20.11911911911912: total elements: 999, unique elements: 208, collisions: 1.0, collisions % 512: 1.2455089820359282, keys min: 2, avg: 6, max: 18
Avg time WyHash, 20.31836836836837: total elements: 999, unique elements: 208, collisions: 1.0, collisions % 512: 1.1751412429378532, keys min: 2, avg: 6, max: 18
Avg time Default, 181.005: total elements: 10, unique elements: 10, collisions: 1.0, collisions % 512: 1.0, keys min: 378, avg: 499, max: 558
Avg time FxHasher, 122.93499999999999: total elements: 10, unique elements: 10, collisions: 1.0, collisions % 512: 1.0, keys min: 378, avg: 499, max: 558
Avg time AHasher, 45.04: total elements: 10, unique elements: 10, collisions: 1.0, collisions % 512: 1.0, keys min: 378, avg: 499, max: 558
Avg time WyHash, 31.005000000000003: total elements: 10, unique elements: 10, collisions: 1.0, collisions % 512: 1.0, keys min: 378, avg: 499, max: 558
Avg time Default, 21.469254658385093: total elements: 161, unique elements: 143, collisions: 1.0, collisions % 512: 1.125984251968504, keys min: 8, avg: 22, max: 43
Avg time FxHasher, 20.225155279503106: total elements: 161, unique elements: 143, collisions: 1.0, collisions % 512: 1.1916666666666667, keys min: 8, avg: 22, max: 43
Avg time AHasher, 20.106832298136645: total elements: 161, unique elements: 143, collisions: 1.0, collisions % 512: 1.125984251968504, keys min: 8, avg: 22, max: 43
Avg time WyHash, 19.890993788819873: total elements: 161, unique elements: 143, collisions: 1.0, collisions % 512: 1.1349206349206349, keys min: 8, avg: 22, max: 43
```

**Swift**

```
Avg time: 85.945, total elements: 100, unique elements: 82, collisions: 1.0, collisions % 512: 1.1388888, keys min: 2, avg: 6, max: 12
Avg time: 67.80245, total elements: 999, unique elements: 203, collisions: 1.0, collisions % 512: 1.2083334, keys min: 1, avg: 5, max: 14
Avg time: 65.73403, total elements: 999, unique elements: 192, collisions: 1.0, collisions % 512: 1.2, keys min: 1, avg: 5, max: 13
Avg time: 240.52744, total elements: 999, unique elements: 532, collisions: 1.0, collisions % 512: 1.4224598, keys min: 1, avg: 6, max: 19
Avg time: 79.92162, total elements: 999, unique elements: 208, collisions: 1.0, collisions % 512: 1.2163743, keys min: 1, avg: 6, max: 18
Avg time: 1773.4, total elements: 10, unique elements: 10, collisions: 1.0, collisions % 512: 1.1111112, keys min: 130, avg: 171, max: 192
Avg time: 140.84721, total elements: 161, unique elements: 143, collisions: 1.0, collisions % 512: 1.0592593, keys min: 8, avg: 22, max: 43
```

**Python**

```
Avg time: 76.5, total elements: 100, unique elements: 82, collisions: 1.0, collisions % 512: 1.1232876712328768, keys min: 2, avg: 6.012195121951219, max: 12
Avg time: 71.52152152152152, total elements: 999, unique elements: 203, collisions: 1.0, collisions % 512: 1.215568862275449, keys min: 1, avg: 5.862068965517241, max: 14
Avg time: 81.63163163163163, total elements: 999, unique elements: 192, collisions: 1.0, collisions % 512: 1.2, keys min: 1, avg: 5.385416666666667, max: 13
Avg time: 79.72972972972973, total elements: 999, unique elements: 532, collisions: 1.0, collisions % 512: 1.592814371257485, keys min: 1, avg: 6.593984962406015, max: 19
Avg time: 78.62862862862863, total elements: 999, unique elements: 208, collisions: 1.0, collisions % 512: 1.2023121387283238, keys min: 1, avg: 6.394230769230769, max: 18
Avg time: 70.0, total elements: 10, unique elements: 10, collisions: 1.0, collisions % 512: 1.0, keys min: 130, avg: 171.4, max: 192
Avg time: 75.77639751552795, total elements: 161, unique elements: 143, collisions: 1.0, collisions % 512: 1.125984251968504, keys min: 8, avg: 22.6013986013986, max: 43
```

**NodeJS**

```
Avg time WyHash: 5024.798, total elements: 100, unique elements: 82, collisions: 1, collisions % 512: 1.0512820512820513, keys min: 2, avg: 6.012195121951219, max: 12
Avg time xxHash: 6870.5885, total elements: 100, unique elements: 82, collisions: 1, collisions % 512: 1.0933333333333333, keys min: 2, avg: 6.012195121951219, max: 12
Avg time WyHash: 3688.6434934934937, total elements: 999, unique elements: 203, collisions: 1, collisions % 512: 1.26875, keys min: 1, avg: 5.862068965517241, max: 14
Avg time xxHash: 4461.4131131131135, total elements: 999, unique elements: 203, collisions: 1, collisions % 512: 1.180232558139535, keys min: 1, avg: 5.862068965517241, max: 14
Avg time WyHash: 3393.779079079079, total elements: 999, unique elements: 192, collisions: 1, collisions % 512: 1.1566265060240963, keys min: 1, avg: 5.385416666666667, max: 13
Avg time xxHash: 4050.333833833834, total elements: 999, unique elements: 192, collisions: 1, collisions % 512: 1.1497005988023952, keys min: 1, avg: 5.385416666666667, max: 13
Avg time WyHash: 4635.906556556557, total elements: 999, unique elements: 532, collisions: 1, collisions % 512: 1.6269113149847094, keys min: 1, avg: 6.593984962406015, max: 19
Avg time xxHash: 5929.1773773773775, total elements: 999, unique elements: 532, collisions: 1, collisions % 512: 1.5880597014925373, keys min: 1, avg: 6.593984962406015, max: 19
Avg time WyHash: 3601.807957957958, total elements: 999, unique elements: 208, collisions: 1, collisions % 512: 1.2093023255813953, keys min: 1, avg: 6.394230769230769, max: 18
Avg time xxHash: 4370.727527527527, total elements: 999, unique elements: 208, collisions: 1, collisions % 512: 1.2682926829268293, keys min: 1, avg: 6.394230769230769, max: 18
Avg time WyHash: 94997.88, total elements: 10, unique elements: 10, collisions: 1, collisions % 512: 1, keys min: 130, avg: 171.4, max: 192
Avg time xxHash: 48261.055, total elements: 10, unique elements: 10, collisions: 1, collisions % 512: 1, keys min: 130, avg: 171.4, max: 192
Avg time WyHash: 6467.241304347826, total elements: 161, unique elements: 143, collisions: 1, collisions % 512: 1.0916030534351144, keys min: 8, avg: 22.6013986013986, max: 43
Avg time xxHash: 5186.903105590062, total elements: 161, unique elements: 143, collisions: 1, collisions % 512: 1.1349206349206349, keys min: 8, avg: 22.6013986013986, max: 43
```

**Go**

```
Avg time: 56.000000, total elements: 100, unique elements: 82, collisions: 1.000000, collisions mod 512: 1.108108, keys min: 2, avg: 6, max: 12 
Avg time: 53.353353, total elements: 999, unique elements: 203, collisions: 1.000000, collisions mod 512: 1.230303, keys min: 1, avg: 5, max: 14 
Avg time: 63.363363, total elements: 999, unique elements: 192, collisions: 1.000000, collisions mod 512: 1.215190, keys min: 1, avg: 5, max: 13 
Avg time: 57.507508, total elements: 999, unique elements: 532, collisions: 1.000000, collisions mod 512: 1.588060, keys min: 2, avg: 12, max: 37 
Avg time: 56.156156, total elements: 999, unique elements: 208, collisions: 1.000000, collisions mod 512: 1.238095, keys min: 2, avg: 6, max: 18 
Avg time: 115.000000, total elements: 10, unique elements: 10, collisions: 1.000000, collisions mod 512: 1.111111, keys min: 378, avg: 499, max: 558 
Avg time: 59.316770, total elements: 161, unique elements: 143, collisions: 1.000000, collisions mod 512: 1.108527, keys min: 8, avg: 22, max: 43 
```

## Benchmark HashMap

This repository also contains a simple HashMap implementation, which allows key to be of type String and value to conform with CollectionElement trait.

### Results

CPU Specs: 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz
Tested with corpus 7, which is a list of S3 actions (total count 161, unique count 143)

```
AHash Avg put time 211.01180124223603
AHash Avg get time 82.304968944099386
WyHash Avg put time 206.67639751552795
WyHash Avg get time 81.214285714285708
FxHash64 Avg put time 223.24844720496895
FxHash64 Avg get time 84.171428571428578
StdHash Avg put time 634.18819875776398
StdHash Avg get time 278.51801242236024
```

MacMini M1, 2020

```
AHash Avg put time 347.82608695652175
AHash Avg get time 162.11180124223603
WyHash Avg put time 363.35403726708074
WyHash Avg get time 192.54658385093168
FxHash64 Avg put time 418.63354037267078
FxHash64 Avg get time 170.80745341614906
StdHash Avg put time 583.22981366459624
StdHash Avg get time 226.08695652173913
```

![Hash map benchmark chart](images/hash_map.png)

--- ahasher/__init__.mojo ---

from .ahasher import ahash, AHasher

--- ahasher/ahasher.mojo ---

from bit import byte_swap
from bit import rotate_bits_left

alias U256 = SIMD[DType.uint64, 4]
alias U128 = SIMD[DType.uint64, 2]
alias MULTIPLE = 6364136223846793005
alias ROT = 23


@always_inline
fn folded_multiply(s: UInt64, by: UInt64) -> UInt64:
    var b1 = s * byte_swap(by)
    var b2 = byte_swap(s) * (~by)
    return b1 ^ byte_swap(b2)


@always_inline
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[DType.uint64]()
            return U128(a, b)
        else:
            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:
        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):
        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 update(inout self, new_data: UInt64):
        self.buffer = folded_multiply(new_data ^ self.buffer, MULTIPLE)

    @always_inline
    fn large_update(inout self, new_data: U128):
        var combined = folded_multiply(
            new_data[0] ^ self.extra_keys[0], new_data[1] ^ self.extra_keys[1]
        )
        self.buffer = rotate_bits_left[ROT]((self.buffer + self.pad) ^ combined)
    
    @always_inline
    fn short_finish(self) -> UInt64:
        return self.buffer + self.pad

    @always_inline
    fn finish(self) -> UInt64:
        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):
        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)

@always_inline
fn ahash(s: String) -> UInt64:
    var length = len(s)
    var b = s.unsafe_ptr()
    var hasher = AHasher(U256(0, 0, 0, 0))

    if length > 8:
        hasher.write(b, length)
    else:
        var value = read_small(b, length)
        hasher.buffer = folded_multiply(value[0] ^ hasher.buffer, value[1] ^ hasher.extra_keys[1])
        hasher.pad = hasher.pad + length
    
    return hasher.finish()


--- benchmark_hash_functions.mojo ---

from collections import Set
from time import now
from memory.unsafe import bitcast
# from fiby_tree import FibyTree
from my_utils import int_cmp64, int_to_str64, cmp_str, stsl, int_cmp, int_to_str, corpus1, corpus2, corpus3, corpus4, corpus5, corpus6, corpus7, corpus8
from ahasher import ahash
from wyhasher import wyhash
from fnv1a import fnv1a64, fnv1a32
from fxhash import fxhash64, fxhash32
from md5 import md5_string

@always_inline
fn std_hash64(s: String) -> UInt64:
    return hash(s)

@always_inline
fn md5_hash(s: String) -> UInt64:
    return bitcast[DType.uint64, 2](md5_string(s))[0]

fn benchamark[hashfn: fn(String) -> UInt64, steps: Int = 20](corpus: List[String], name: StringLiteral, ):
    # var f = FibyTree[UInt64, int_cmp64, int_to_str64]()
    # var f1 = FibyTree[UInt64, int_cmp64, int_to_str64]()
    var fs = Set[String]()
    var min_avg: Float64 = 100000.0
    var mod = (1 << 9)
    var hashes = List[UInt64]()
    var mod_hashes: List[UInt64] = List[UInt64]()
    var total = 0
    for step in range(steps):
        for i in range(len(corpus)):
            var key = corpus[i]
            var tik = now()
            var hash = hashfn(key)
            var tok = now()
            # hash_total += hash
            total += tok - tik
            var found = False
            for i in range(len(hashes)):
                if hash == hashes[i]:
                    found = True
                    break
            if not found:
                hashes.append(hash)
            found = False
            for i in range(len(mod_hashes)):
                if  hash & (mod - 1) == mod_hashes[i]:
                    found = True
                    break
            if not found:
                mod_hashes.append(hash & (mod - 1))# f.add(hash)
            # f1.add(hash & (mod - 1))
            if step == 0:
                fs.add(key)
    var c_avg = (total / steps) / len(corpus)
    min_avg = min(min_avg, c_avg)
    print(
        name, "avg hash compute", min_avg, "| hash colision", len(fs) / len(hashes),
        "| hash colision mod", mod, len(fs) /  len(mod_hashes)
    )

fn benchamark32[hashfn: fn(String) -> UInt32, steps: Int = 20](corpus: List[String], name: StringLiteral):
    # var f = FibyTree[UInt32, int_cmp, int_to_str]()
    # var f1 = FibyTree[UInt32, int_cmp, int_to_str]()
    var fs = Set[String]()
    var min_avg: Float64 = 100000.0
    var mod = (1 << 9)
    var hashes: List[UInt32] = List[UInt32]()
    var mod_hashes: List[UInt32] = List[UInt32]()
    var total = 0
    for step in range(steps):
        for i in range(len(corpus)):
            var key = corpus[i]
            var tik = now()
            var hash = hashfn(key)
            var tok = now()
            total += tok - tik
            var found = False
            for i in range(len(hashes)):
                if hash == hashes[i]:
                    found = True
                    break
            if not found:
                hashes.append(hash)
            found = False
            for i in range(len(mod_hashes)):
                if  hash & (mod - 1) == mod_hashes[i]:
                    found = True
                    break
            if not found:
                mod_hashes.append(hash & (mod - 1))
            # f.add(hash)
            # f1.add(hash & (mod - 1))
            if step == 0:
                fs.add(key)
    var c_avg = (total / steps) / len(corpus)
    min_avg = min(min_avg, c_avg)
    print(
        name, "avg hash compute", min_avg, "| hash colision", len(fs) / len(hashes), 
        "| hash colision mod", mod, len(fs) /  len(mod_hashes)
    )


fn corpus_details(corpus: List[String]):
    var word_count = len(corpus)
    print(word_count)
    var fs = Set[String]()
    var min_key_size = 10000000
    var max_key_size = 0
    var total_key_size = 0
    for i in range(word_count):
        var key = corpus[i]
        fs.add(key)
        var key_size = len(key)
        total_key_size += key_size
        min_key_size = min(min_key_size, key_size)
        max_key_size = max(max_key_size, key_size)

    print(
        "Word count", word_count, "| unique word count", 
        fs.__len__(), 
        "| min key size", min_key_size, "| avg key size", total_key_size / word_count, "| max key size", max_key_size
    )


fn sample_wyhash(s : String) -> UInt64:
    var default_secret = SIMD[DType.uint64, 4](0xa0761d6478bd642f, 0xe7037ed1a0b428db, 0x8ebc6af09c88c6e3, 0x589965cc75374cc3)
    return wyhash(s, 0, default_secret)

fn sample_fxhash64(s : String) -> UInt64:
    return fxhash64(s, 0)

fn sample_fxhash32(s : String) -> UInt32:
    return fxhash32(s, 0)

fn main() raises:
    var c1 = corpus1()
    print("\nCorpus 1")
    corpus_details(c1)
    benchamark[ahash](c1, "AHash")
    benchamark[sample_wyhash](c1, "Wyhash")
    benchamark32[fnv1a32](c1, "fnv1a32")
    benchamark[fnv1a64](c1, "fnv1a64")
    benchamark32[sample_fxhash32](c1, "fxHash32")
    benchamark[sample_fxhash64](c1, "fxHash64")
    benchamark[std_hash64](c1, "std_Hash64")
    benchamark[md5_hash](c1, "MD5")

    var c2 = corpus2()
    print("\nCorpus 2")
    corpus_details(c2)
    benchamark[ahash](c2, "AHash")
    benchamark[sample_wyhash](c2, "Wyhash")
    benchamark32[fnv1a32](c2, "fnv1a32")
    benchamark[fnv1a64](c2, "fnv1a64")
    benchamark32[sample_fxhash32](c2, "fxHash32")
    benchamark[sample_fxhash64](c2, "fxHash64")
    benchamark[std_hash64](c2, "std_Hash64")
    benchamark[md5_hash](c2, "MD5")
    
    var c3 = corpus3()
    print("\nCorpus 3")
    corpus_details(c3)
    benchamark[ahash](c3, "AHash")
    benchamark[sample_wyhash](c3, "Wyhash")
    benchamark32[fnv1a32](c3, "fnv1a32")
    benchamark[fnv1a64](c3, "fnv1a64")
    benchamark32[sample_fxhash32](c3, "fxHash32")
    benchamark[sample_fxhash64](c3, "fxHash64")
    benchamark[std_hash64](c3, "std_Hash64")
    benchamark[md5_hash](c3, "MD5")

    var c4 = corpus4()
    print("\nCorpus 4")
    corpus_details(c4)
    benchamark[ahash](c4, "AHash")
    benchamark[sample_wyhash](c4, "Wyhash")
    benchamark32[fnv1a32](c4, "fnv1a32")
    benchamark[fnv1a64](c4, "fnv1a64")
    benchamark32[sample_fxhash32](c4, "fxHash32")
    benchamark[sample_fxhash64](c4, "fxHash64")
    benchamark[std_hash64](c4, "std_Hash64")
    benchamark[md5_hash](c4, "MD5")

    var c5 = corpus5()
    print("\nCorpus 5")
    corpus_details(c5)
    benchamark[ahash](c5, "AHash")
    benchamark[sample_wyhash](c5, "Wyhash")
    benchamark32[fnv1a32](c5, "fnv1a32")
    benchamark[fnv1a64](c5, "fnv1a64")
    benchamark32[sample_fxhash32](c5, "fxHash32")
    benchamark[sample_fxhash64](c5, "fxHash64")
    benchamark[std_hash64](c5, "std_Hash64")
    benchamark[md5_hash](c5, "MD5")

    var c6 = corpus6()
    print("\nCorpus 6")
    corpus_details(c6)
    benchamark[ahash](c6, "AHash")
    benchamark[sample_wyhash](c6, "Wyhash")
    benchamark32[fnv1a32](c6, "fnv1a32")
    benchamark[fnv1a64](c6, "fnv1a64")
    benchamark32[sample_fxhash32](c6, "fxHash32")
    benchamark[sample_fxhash64](c6, "fxHash64")
    benchamark[std_hash64](c6, "std_Hash64")
    benchamark[md5_hash](c6, "MD5")

    var c7 = corpus7()
    print("\nCorpus 7")
    corpus_details(c7)
    benchamark[ahash](c7, "AHash")
    benchamark[sample_wyhash](c7, "Wyhash")
    benchamark32[fnv1a32](c7, "fnv1a32")
    benchamark[fnv1a64](c7, "fnv1a64")
    benchamark32[sample_fxhash32](c7, "fxHash32")
    benchamark[sample_fxhash64](c7, "fxHash64")
    benchamark[std_hash64](c7, "std_Hash64")
    benchamark[md5_hash](c7, "MD5")

    var c8 = corpus8()
    print("\nCorpus 8")
    corpus_details(c8)
    benchamark[ahash, 1](c8, "AHash")
    benchamark[sample_wyhash, 1](c8, "Wyhash")
    benchamark32[fnv1a32, 1](c8, "fnv1a32")
    benchamark[fnv1a64, 1](c8, "fnv1a64")
    benchamark32[sample_fxhash32, 1](c8, "fxHash32")
    benchamark[sample_fxhash64, 1](c8, "fxHash64")
    benchamark[std_hash64, 1](c8, "std_Hash64")
    # benchamark[md5_hash, 1](c8, "MD5")


--- benchmark_hash_words_file.mojo ---

from time import now
from md5 import md5_string
from wyhasher import wyhash
from ahasher import ahash
from fxhash import fxhash64
from sha import sha256_encode

import benchmark
from benchmark import Unit
from pathlib import Path
from collections.vector import InlinedFixedVector

fn to_hex(digest: InlinedFixedVector[UInt8, 32]) -> String:
    var lookup = String("0123456789abcdef")
    var result: String = ""
    for i in range(len(digest)):
        var v = int(digest[i])
        result += lookup[(v >> 4)]
        result += lookup[v & 15]
    return result

fn to_hex(digest: SIMD[DType.uint8, 16]) -> String:
    var lookup = String("0123456789abcdef")
    var result: String = ""
    for i in range(len(digest)):
        var v = int(digest[i])
        result += lookup[(v >> 4)]
        result += lookup[v & 15]
    return result

fn main() raises:
    var text = Path("/usr/share/dict/words").read_text()
    var tik = now()
    var h0 = md5_string(text)
    var tok = now()
    print("MD5     :", tok - tik, to_hex(h0), len(text))
    
    tik = now()
    var h5 = sha256_encode(text.unsafe_ptr(), 0)
    tok = now()
    print("SHA256  :", tok - tik, to_hex(h5), len(text))

    tik = now()
    var h1 = wyhash(text, 0)
    tok = now()
    print("Wyhash  :", tok - tik, h1, len(text))

    tik = now()
    var h2 = ahash(text)
    tok = now()
    print("Ahash   :", tok - tik, h2, len(text))

    tik = now()
    var h3 = fxhash64(text)
    tok = now()
    print("Fxhash  :", tok - tik, h3, len(text))

    tik = now()
    var h4 = hash(text.unsafe_ptr(), len(text))
    tok = now()
    print("Std hash:", tok - tik, h4, len(text))

    var hb = SIMD[DType.uint8, 16]()

    @parameter
    fn md5_test():
        hb = md5_string(text)
    print("===MD5===")
    var report0 = benchmark.run[md5_test]()
    report0.print(Unit.ns)
    print(hb)
    
    var hi = 0

    @parameter
    fn hash_test():
        hi = hash(text.unsafe_ptr(), len(text))

    print("===Std hash===")
    var report1 = benchmark.run[hash_test]()
    report1.print(Unit.ns)
    print(hi)

    var hu = UInt64(0)

    @parameter
    fn ahash_test():
        hu = ahash(text)

    print("===Ahash===")
    var report2 = benchmark.run[ahash_test]()
    report2.print(Unit.ns)
    print(hu)

    @parameter
    fn wyhash_test():
        hu = wyhash(text, 0)

    print("===Wyhash===")
    var report3 = benchmark.run[wyhash_test]()
    report3.print(Unit.ns)
    print(hu)
    
    _ = text


--- benchmark_other_languages/c/hash_functions/Makefile ---

# wyhash bench makefile

CXX = g++
CXXFLAGS = -std=c++11 -O2 -s -Wall -march=native

TARGETS = wyhash0 wyhash1 wyhash2 xxh3scalar xxh3sse2 xxh3avx2
 
all:	$(TARGETS)

wyhash0:	benchmark.cpp wyhash.h
	$(CXX) benchmark.cpp -o wyhash0 $(CXXFLAGS) -DWYHASH_CONDOM=0

wyhash1:	benchmark.cpp wyhash.h
	$(CXX) benchmark.cpp -o wyhash1 $(CXXFLAGS) -DWYHASH_CONDOM=1

wyhash2:	benchmark.cpp wyhash.h
	$(CXX) benchmark.cpp -o wyhash2 $(CXXFLAGS) -DWYHASH_CONDOM=2

xxh3scalar:	benchmark.cpp
	$(CXX) benchmark.cpp -o xxh3scalar $(CXXFLAGS) -DXXH_VECTOR=0 -DXXH3

xxh3sse2:	benchmark.cpp
	$(CXX) benchmark.cpp -o xxh3sse2 $(CXXFLAGS) -DXXH_VECTOR=1 -DXXH3

xxh3avx2:	benchmark.cpp
	$(CXX) benchmark.cpp -o xxh3avx2 $(CXXFLAGS) -DXXH_VECTOR=2 -DXXH3

clean:
	rm $(TARGETS)


--- benchmark_other_languages/c/hash_functions/benchmark.cpp ---

#include <sys/time.h>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <stdint.h>
#include <string>
#include <cstring>
#include <vector>
#include <string>
#include <sstream>

#ifndef XXH3
#include "wyhash.h"
#else
#include "xxh3.h"
#endif

using namespace std;

void benchmark(stringstream *corpus, string name, char delimeter) {
    string key;
    uint64_t dummy = 0;
    uint64_t total = 0;
    timeval tik, tok;
    uint64_t count = 0;
    for (size_t i = 0; i < 20; i++)
    {   
        while(getline(*corpus, key, delimeter))
        {
            gettimeofday(&tik,NULL);
            #ifndef XXH3
            auto h = wyhash(key.c_str(),key.size(),0,_wyp);
            #else
            auto h = XXH3_64bits_withSeed(key.c_str(),key.size(),0);
            #endif
            gettimeofday(&tok,NULL);
            dummy += h;
            total += (tok.tv_usec-tik.tv_usec) * 1000;
            count ++;
        }
        corpus->clear();
        corpus->seekg(0,ios::beg);
    }
    
    
    cout << "Avg per key " << name << ": "  << double(total) / count << " " << count << " " << dummy << endl;
}

int main(int argc, char **argv)
{
    stringstream corpus1("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque orci urna, pretium et porta ac, porttitor sit amet sem. Fusce sagittis lorem neque, vitae sollicitudin elit suscipit et. In interdum convallis nisl in ornare. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aliquam erat volutpat. Morbi mollis iaculis lectus ac tincidunt. Fusce nisi lacus, semper eu dignissim et, malesuada non mi. Sed euismod urna vel elit faucibus, eu bibendum ante fringilla. Curabitur tempus in turpis at mattis. Aliquam erat volutpat. Donec maximus elementum felis, sit amet dignissim augue tincidunt blandit. Aliquam fermentum, est eu mollis.");
    stringstream corpus2("But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains. But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains.But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection:");
    stringstream corpus3("A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions! A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions! A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions!A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls");
    stringstream corpus4("Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое. Лежа на панцирнотвердой спине, он видел, стоило ему приподнять голову, свой коричневый, выпуклый, разделенный дугообразными чешуйками живот, на верхушке которого еле держалось готовое вот-вот окончательно сползти одеяло. Его многочисленные, убого тонкие по сравнению с остальным телом ножки беспомощно копошились у него перед глазами. «Что со мной случилось?» – подумал он. Это не было сном. Его комната, настоящая, разве что слишком маленькая, но обычная комната, мирно покоилась в своих четырех хорошо знакомых стенах. Над столом, где были разложены распакованные образцы сукон – Замза был коммивояжером, – висел портрет, который он недавно вырезал из иллюстрированного журнала и вставил в красивую золоченую рамку. На портрете была изображена дама в меховой шляпе и боа, она сидела очень прямо и протягивала зрителю тяжелую меховую муфту, в которой целиком исчезала ее рука. Затем взгляд Грегора устремился в окно, и пасмурная погода – слышно было, как по жести подоконника стучат капли дождя – привела его и вовсе в грустное настроение. «Хорошо бы еще немного поспать и забыть всю эту чепуху», – подумал он, но это было совершенно неосуществимо, он привык спать на правом боку, а в теперешнем своем состоянии он никак не мог принять этого положения. С какой бы силой ни поворачивался он на правый бок, он неизменно сваливался опять на спину. Закрыв глаза, чтобы не видеть своих барахтающихся ног, он проделал это добрую сотню раз и отказался от этих попыток только тогда, когда почувствовал какую-то неведомую дотоле, тупую и слабую боль в боку. «Ах ты, господи, – подумал он, – какую я выбрал хлопотную профессию! Изо дня в день в разъездах. Деловых волнений куда больше, чем на месте, в торговом доме, а кроме того, изволь терпеть тяготы дороги, думай о расписании поездов, мирись с плохим, нерегулярным питанием, завязывай со все новыми и новыми людьми недолгие, никогда не бывающие сердечными отношения. Черт бы побрал все это!» Он почувствовал вверху живота легкий зуд; медленно подвинулся на спине к прутьям кровати, чтобы удобнее было поднять голову; нашел зудевшее место, сплошь покрытое, как оказалось, белыми непонятными точечками; хотел было ощупать это место одной из ножек, но сразу отдернул ее, ибо даже простое прикосновение вызвало у него, Грегора, озноб. Он соскользнул в прежнее свое положение. «От этого раннего вставания, – подумал он, – можно совсем обезуметь. Человек должен высыпаться. Другие коммивояжеры живут, как одалиски. Когда я, например, среди дня возвращаюсь в гостиницу, чтобы переписать полученные заказы, эти господа только завтракают. А осмелься я вести себя так, мои хозяин выгнал бы меня сразу. Кто знает, впрочем, может быть, это было бы даже очень хорошо для меня. Если бы я не сдерживался ради родителей, я бы давно заявил об уходе, я бы подошел к своему хозяину и выложил ему все, что о нем думаю. Он бы так и свалился с конторки! Странная у него манера – садиться на конторку и с ее высоты разговаривать со служащим, который вдобавок вынужден подойти вплотную к конторке из-за того, что хозяин туг на ухо. Однако надежда еще не совсем потеряна: как только я накоплю денег, чтобы выплатить долг моих родителей – на это уйдет еще лет пять-шесть, – я так и поступлю. Тут-то мы и распрощаемся раз и навсегда. А пока что надо подниматься, мой поезд отходит в пять». И он взглянул на будильник, который тикал на сундуке. «Боже правый!» – подумал он. Было половина седьмого, и стрелки спокойно двигались дальше, было даже больше половины, без малого уже три четверти. Неужели будильник не звонил? С кровати было видно, что он поставлен правильно, на четыре часа; и он, несомненно, звонил. Но как можно было спокойно спать под этот сотрясающий мебель трезвон? Ну, спал-то он неспокойно, но, видимо, крепко. Однако что делать теперь? Следующий поезд уходит в семь часов; чтобы поспеть на него, он должен отчаянно торопиться, а набор образцов еще не упакован, да и сам он отнюдь не чувствует себя свежим и легким на подъем. И даже поспей он на поезд, хозяйского разноса ему все равно не избежать – ведь рассыльный торгового дома дежурил у пятичасового поезда и давно доложил о его, Грегора, опоздании. Рассыльный, человек бесхарактерный и неумный, был ставленником хозяина. А что, если сказаться больным? Но это было бы крайне неприятно и показалось бы подозрительным, ибо за пятилетнюю свою службу Грегор ни разу еще не болел. Хозяин, конечно, привел бы врача больничной кассы и стал попрекать родителей сыном-лентяем, отводя любые возражения ссылкой на этого врача, по мнению которого все люди на свете совершенно здоровы и только не любят работать. И разве в данном случае он был бы так уж неправ? Если не считать сонливости, действительно странной после такого долгого сна, Грегор и в самом деле чувствовал себя превосходно и был даже чертовски голоден.Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое. Лежа на панцирнотвердой спине, он видел, стоило ему приподнять голову, свой коричневый, выпуклый, разделенный дугообразными чешуйками живот, на верхушке которого еле держалось готовое вот-вот окончательно сползти одеяло. Его многочисленные, убого тонкие по сравнению с остальным телом ножки беспомощно копошились у него перед глазами. «Что со мной случилось?» – подумал он. Это не было сном. Его комната, настоящая, разве что слишком маленькая, но обычная комната, мирно покоилась в своих четырех хорошо знакомых стенах. Над столом, где были разложены распакованные образцы сукон – Замза был коммивояжером, – висел портрет, который он недавно вырезал из иллюстрированного журнала и вставил в красивую золоченую рамку. На портрете была изображена дама в меховой шляпе и боа, она сидела очень прямо и протягивала зрителю тяжелую меховую муфту, в которой целиком исчезала ее рука. Затем взгляд Грегора устремился в окно, и пасмурная погода – слышно было, как по жести подоконника стучат капли дождя – привела его и вовсе в грустное настроение. «Хорошо бы еще немного поспать и забыть всю эту чепуху», – подумал он, но это было совершенно неосуществимо, он привык спать на правом боку, а в теперешнем своем состоянии он никак не мог принять этого положения. С какой бы силой ни поворачивался он на правый бок, он неизменно сваливался опять на спину.");
    stringstream corpus5("Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch. Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch. Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch.Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen");
    stringstream corpus6("米くを舵4物委らご氏松ハナテフ月関ソ時平ふいの博情れじフ牟万い元56園フメヤオ試図ロツヤ未備王こと傷喫羅踊んゆし。栃ユヱオ書著作ユソツロ英祉業ア大課ご権質フべ空8午キ切軟づン著郎そゃす格町採ヱオマコ処8付国ムハチア究表でなだ際無ロミヱ地兵ぴげ庭体すク発抜爆位や。楽富むゆず盛航カナセ携代ハ本高きた員59今骸ンラえぜ城解イケ穴訴ぽぎ属住ヤケトヌ抱点ト広注厚でて。 国リ出難セユメ軍手ヘカウ画形サヲシ猛85用ヲキミ心死よしと身処ケヨミオ教主ーぽ事業んく字国たさょ図能シミスヤ社8板ル岡世58次戒知院んれり。市メ誘根カ数問禁竹ゃれえみ給辺のでみき今二ぎさ裕止過こクすと無32郎所ラた生展ヌヘス成度慣葬勇厘ばてか。室ゃ下携疲ム色権がぽりっ銃週ノオ姫千テム健蔵い研手ッ放容ル告属め旅側26企サノヨ宅都福ぞ通待ちぴね種脳イど労希望義通むン。 罰しい続負せ著低たル異師ユハワ東添質コチ転集ルヤ雇聴約ヒ前統らた情厳ゆさでや真胸や有披暑棚豆ゆぼたけ。盛ワセロナ情競クるっわ講3音ずをせ少地めしぜょ手63明視れに判企ヒヌエソ求総58特本ね井比ユラキ禁頭馬るゅリす能率率かがさわ。葉サソ医郡ヱヘソ労帰ナケスミ救写ワヘ株審ネヒニミ安逮イ人画ラ涯車はラ極騒りなド件5級ンかふー劇41著ぱぐ凱討だ文世ぶづどま界善魅マ渓経競融れがや。 連ーぜらご模分ッ視外ばフく運発群ほぼづ育越一ほごクけ案募ヲイソ治会イせフ製君ぜた漢村1変リヒ構5際ツ御文ヲ臭入さドぼ代書ハケ引技ろみれ回観注倉徹ぱ。論ラづ海要サ情座ゃり齢宣ラモエ芸化エマホ覧催回ら戦69本外ト葬岳な政画か連針ぴリフず。約ル闘辺ぽ経2応掲ホサアラ塾小コラ画決クノオ上室レヌヱ勝逮ぜるえむ責豊チノ明意ひけ訟6碁草メタチエ財午召喝塊む。 決めでわ名金つけレわ続人県約ぽぼす尾腹ユサ戦載リシ護賀レモフツ重涯ニ治者むんっみ職更カタチレ提話2何ワ責東まけげふ能政ヌ供禁がびてわ提改倶れめ。読み担後ぽ安加ぎ論鹿ツ統最お気麻月つじもあ竜思いろめ判必満理トコ文連ムイウハ寄串ざほびー。文ゆこっ向27年メイ便能ノセヲ待1王スねたゆ伝派んね点過カト治読よにきべ使人スシ都言え阻8割べづえみ注引敷的岳犠眠どそ。 学用イだ医客開ロ供界もぞだ実隆モイヌ務坂ナコヲ権野ろづ初場ぱ低会づぱじ新倒コ化政レ止奮浸猪ッわえづ。形いやリ要帰ほまむだ業領スル必打さ島14巻リ集日ネヘホタ面幅ち写上そぴ円図ムタコモ報使イわざと会催ヤヲ康証をドぶレ盤岡ホハツ作29管しをめ公問懐蓄っさ。来ゆぼあぱ投秋シ語右ぐ身靖かば辛握捕家記ヘワ神岐囲づ毘観メテクツ政73夕罪57需93誌飲査仁さ。 変レめ束球よんま会特ヱコ聞重だ史純ーどる件32浦レぴよゃ上強ネラリロ査従セユヤ専棋光レ作表ひぶ予正ぜーな誉確フス函6報円ス進治ね能営済否雄でわょ。42生型ば着続ア短実ぎおめび前環闘ラヤヲル診均っとにの声公トヱテマ整試椅情久妊舌頃ざとっく。品キチトテ阿国ラら受87世ヲフセリ川86個ーょぼげ危子ヘレカメ無会ぱかへ事通んかて電条ロツ徴商ぶぞそを居暑メ害広せもがり禁応レミヲ応響割壮憶はぱ。 千れンが織財メニ況界ネトレミ学豊フオホシ近月レたやご的罪ょな菱技ちる警栗エセ提89林危氷48参ア説森クキヒヱ薬社ホコエリ負和ルび紀下ケミイ掲歳特ごず扱底ク護木連ちクを各形ばすか。変ぱなれ町7融ヌ街準以タユヘム質裕ぶで遺語俊ぎずょ事金文キ写多山ーゆに歩帯すで会世クぜよ論写ヲ達71林危氷5間続ぎぜび高怠す。 係8青け応著ミ戦条ナヘネカ思79未ぎ算伊をゃ泉人ーづ需説っ畑鹿27軽ラソツ権2促千護ルロナカ開国ケ暴嶋ご池表だ。佐フナ訪麻はてせば勝効をあ医戦画とさわぴ者両すいあ並来んば載食ぴ件友頂業へえぞ魚祝ネラ聞率スコリケ始全ンこび夫出ドふ今布うぎふゅ実克即哉循やしんな。 暮す備54依紀てッん末刊と柔称むてス無府ケイ変壌をぱ汁連フマス海世ヌ中負知問ナヘケ純推ひ読着ヒ言若私軽れ。掲けフむ王本オコ線人をっさ必和断セソヲハ図芸ちかな防長りぶは投新意相ツ並5余セ職岳ぞ端古空援そ。森ヨエチ題5東っ自兄ち暴5近鹿横ト的京ハ安氷ナキ深際ぎ並節くスむの権工ほルせ京49効タムチ処三ぞぴラ済国ずっ文経ヘトミ水分準そが。");
    stringstream corpus7("AbortMultipartUpload CompleteMultipartUpload CopyObject CreateBucket CreateMultipartUpload DeleteBucket DeleteBucketAnalyticsConfiguration DeleteBucketCors DeleteBucketEncryption DeleteBucketIntelligentTieringConfiguration DeleteBucketInventoryConfiguration DeleteBucketLifecycle DeleteBucketMetricsConfiguration DeleteBucketOwnershipControls DeleteBucketPolicy DeleteBucketReplication DeleteBucketTagging DeleteBucketWebsite DeleteObject DeleteObjects DeleteObjectTagging DeletePublicAccessBlock GetBucketAccelerateConfiguration GetBucketAcl GetBucketAnalyticsConfiguration GetBucketCors GetBucketEncryption GetBucketIntelligentTieringConfiguration GetBucketInventoryConfiguration GetBucketLifecycle GetBucketLifecycleConfiguration GetBucketLocation GetBucketLogging GetBucketMetricsConfiguration GetBucketNotification GetBucketNotificationConfiguration GetBucketOwnershipControls GetBucketPolicy GetBucketPolicyStatus GetBucketReplication GetBucketRequestPayment GetBucketTagging GetBucketVersioning GetBucketWebsite GetObject GetObjectAcl GetObjectAttributes GetObjectLegalHold GetObjectLockConfiguration GetObjectRetention GetObjectTagging GetObjectTorrent GetPublicAccessBlock HeadBucket HeadObject ListBucketAnalyticsConfigurations ListBucketIntelligentTieringConfigurations ListBucketInventoryConfigurations ListBucketMetricsConfigurations ListBuckets ListMultipartUploads ListObjects ListObjectsV2 ListObjectVersions ListParts PutBucketAccelerateConfiguration PutBucketAcl PutBucketAnalyticsConfiguration PutBucketCors PutBucketEncryption PutBucketIntelligentTieringConfiguration PutBucketInventoryConfiguration PutBucketLifecycle PutBucketLifecycleConfiguration PutBucketLogging PutBucketMetricsConfiguration PutBucketNotification PutBucketNotificationConfiguration PutBucketOwnershipControls PutBucketPolicy PutBucketReplication PutBucketRequestPayment PutBucketTagging PutBucketVersioning PutBucketWebsite PutObject PutObjectAcl PutObjectLegalHold PutObjectLockConfiguration PutObjectRetention PutObjectTagging PutPublicAccessBlock RestoreObject SelectObjectContent UploadPart UploadPartCopy WriteGetObjectResponse CreateAccessPoint CreateAccessPointForObjectLambda CreateBucket CreateJob CreateMultiRegionAccessPoint DeleteAccessPoint DeleteAccessPointForObjectLambda DeleteAccessPointPolicy DeleteAccessPointPolicyForObjectLambda DeleteBucket DeleteBucketLifecycleConfiguration DeleteBucketPolicy DeleteBucketReplication DeleteBucketTagging DeleteJobTagging DeleteMultiRegionAccessPoint DeletePublicAccessBlock DeleteStorageLensConfiguration DeleteStorageLensConfigurationTagging DescribeJob DescribeMultiRegionAccessPointOperation GetAccessPoint GetAccessPointConfigurationForObjectLambda GetAccessPointForObjectLambda GetAccessPointPolicy GetAccessPointPolicyForObjectLambda GetAccessPointPolicyStatus GetAccessPointPolicyStatusForObjectLambda GetBucket GetBucketLifecycleConfiguration GetBucketPolicy GetBucketReplication GetBucketTagging GetBucketVersioning GetJobTagging GetMultiRegionAccessPoint GetMultiRegionAccessPointPolicy GetMultiRegionAccessPointPolicyStatus GetMultiRegionAccessPointRoutes GetPublicAccessBlock GetStorageLensConfiguration GetStorageLensConfigurationTagging ListAccessPoints ListAccessPointsForObjectLambda ListJobs ListMultiRegionAccessPoints ListRegionalBuckets ListStorageLensConfigurations PutAccessPointConfigurationForObjectLambda PutAccessPointPolicy PutAccessPointPolicyForObjectLambda PutBucketLifecycleConfiguration PutBucketPolicy PutBucketReplication PutBucketTagging PutBucketVersioning PutJobTagging PutMultiRegionAccessPointPolicy PutPublicAccessBlock PutStorageLensConfiguration PutStorageLensConfigurationTagging SubmitMultiRegionAccessPointRoutes UpdateJobPriority UpdateJobStatus");
    stringstream corpus8;

    ifstream file( "/usr/share/dict/words" );
    corpus8 << file.rdbuf();
    file.close();

    #ifndef XXH3
    string name = "Wyhash";
    #else
    string name = "XXH3";
    #endif

    benchmark(&corpus1, name, ' ');
    benchmark(&corpus2, name, ' ');
    benchmark(&corpus3, name, ' ');
    benchmark(&corpus4, name, ' ');
    benchmark(&corpus5, name, ' ');
    benchmark(&corpus6, name, ' ');
    benchmark(&corpus7, name, ' ');
    benchmark(&corpus8, name, '\n');

    // string key = "Maxim";
    // auto h = wyhash(key.c_str(),key.size(),0,_wyp);
    // cout << key << ": " << h << endl;
    return 0;
}

--- benchmark_other_languages/c/hash_functions/benchmark_wyhash.cpp ---

#include <sys/time.h>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <stdint.h>
#include <string>
#include <cstring>
#include <vector>
#ifndef XXH3
#include "wyhash.h"
#else
#include "xxh3.h"
#endif

using namespace std;

struct ha
{
    size_t operator()(const string &s, uint64_t seed)const
    {
        #ifndef XXH3
        return wyhash(s.c_str(),s.size(),seed,_wyp);
        #else
        return XXH3_64bits_withSeed(s.c_str(),s.size(),seed);
        #endif
    }
};

vector <string> v;
template <class Hasher>
uint64_t bench_hash(const char *name)
{
    Hasher h;
    string s;
    timeval beg, end;
    uint64_t dummy=0;
    const uint64_t N=v.size(), R=0x1000;

    cerr.precision(2);
    cerr.setf(ios::fixed);
    cerr<<'|'<<name<<(strlen(name)<8?"\t\t|":"\t|");

    gettimeofday(&beg,NULL);
    for(size_t r=0; r<R; r++)
        for(size_t i=0; i<N; i++)
            dummy+=h(v[i],r);
    gettimeofday(&end,NULL);
    cerr<<1e-6*R*N/(end.tv_sec-beg.tv_sec+1e-6*(end.tv_usec-beg.tv_usec))<<"\t\t|";

    s.resize(1<<8);
    gettimeofday(&beg,NULL);
    for(size_t r=0; r<(R<<14); r++)
        dummy+=h(s,r);
    gettimeofday(&end,NULL);
    cerr<<1e-9*(R<<14)*s.size()/(end.tv_sec-beg.tv_sec+1e-6*(end.tv_usec-beg.tv_usec))<<"\t\t|";

    s.resize(1<<16);
    gettimeofday(&beg,NULL);
    for(size_t r=0; r<(R<<6); r++)
        dummy+=h(s,r);
    gettimeofday(&end,NULL);
    cerr<<1e-9*(R<<6)*s.size()/(end.tv_sec-beg.tv_sec+1e-6*(end.tv_usec-beg.tv_usec))<<"\t\t|\n";
    return dummy;
}

int main(int argc, char **argv)
{
    string file_name="/usr/share/dict/words", help_s="-h", s;
    if(argc>1)
    {
        if(help_s.compare(argv[1])==0)
        {
            cout<<"usage:\n"<<argv[0]<<" <file>\n";
            cout<<"if no arguments given \'"<<file_name<<"\' is used\n";
            return 0;
        }
        file_name=argv[1];
    }
    ifstream fi(file_name.c_str());
    if(fi.fail())
    {
        cout<<"File \'"<<file_name<<"\' not found.\n";
        cout<<"Try `-h\' for help.\n";
        return 1;
    }
    for(fi>>s; !fi.eof(); fi>>s)
        if(s.size())
            v.push_back(s);
    fi.close();
    //shuffle the array to benchmark random access
    for(size_t i=v.size()-1; i; i--)
        swap(v[i],v[rand()%(i+1)]);

    uint64_t r=0;
    cerr<<file_name<<'\n';
    cerr<<"|hash function\t|short hash/us\t|bulk_256B GB/s\t|bulk_64KB GB/s\t|\n";
    cerr<<"|----\t\t|----\t\t|----\t\t|----\t\t|\n";

    #ifndef XXH3
    r+=bench_hash<ha>("wyhash");
    #else
    r+=bench_hash<ha>("xxh3");
    #endif

    return r;
}

--- benchmark_other_languages/c/hash_functions/wyhash.h ---

// This is free and unencumbered software released into the public domain under The Unlicense (http://unlicense.org/)
// main repo: https://github.com/wangyi-fudan/wyhash
// author: 王一 Wang Yi <godspeed_china@yeah.net>
// contributors: Reini Urban, Dietrich Epp, Joshua Haberman, Tommy Ettinger, Daniel Lemire, Otmar Ertl, cocowalla, leo-yuriev, Diego Barrios Romero, paulie-g, dumblob, Yann Collet, ivte-ms, hyb, James Z.M. Gao, easyaspi314 (Devin), TheOneric

/* quick example:
   string s="fjsakfdsjkf";
   uint64_t hash=wyhash(s.c_str(), s.size(), 0, _wyp);
*/

#ifndef wyhash_final_version_4_2
#define wyhash_final_version_4_2

#ifndef WYHASH_CONDOM
//protections that produce different results:
//1: normal valid behavior
//2: extra protection against entropy loss (probability=2^-63), aka. "blind multiplication"
#define WYHASH_CONDOM 1
#endif

#ifndef WYHASH_32BIT_MUM
//0: normal version, slow on 32 bit systems
//1: faster on 32 bit systems but produces different results, incompatible with wy2u0k function
#define WYHASH_32BIT_MUM 0  
#endif

//includes
#include <stdint.h>
#include <string.h>
#if defined(_MSC_VER) && defined(_M_X64)
  #include <intrin.h>
  #pragma intrinsic(_umul128)
#endif

//likely and unlikely macros
#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__)
  #define _likely_(x)  __builtin_expect(x,1)
  #define _unlikely_(x)  __builtin_expect(x,0)
#else
  #define _likely_(x) (x)
  #define _unlikely_(x) (x)
#endif

//128bit multiply function
static inline uint64_t _wyrot(uint64_t x) { return (x>>32)|(x<<32); }
static inline void _wymum(uint64_t *A, uint64_t *B){
#if(WYHASH_32BIT_MUM)
  uint64_t hh=(*A>>32)*(*B>>32), hl=(*A>>32)*(uint32_t)*B, lh=(uint32_t)*A*(*B>>32), ll=(uint64_t)(uint32_t)*A*(uint32_t)*B;
  #if(WYHASH_CONDOM>1)
  *A^=_wyrot(hl)^hh; *B^=_wyrot(lh)^ll;
  #else
  *A=_wyrot(hl)^hh; *B=_wyrot(lh)^ll;
  #endif
#elif defined(__SIZEOF_INT128__)
  __uint128_t r=*A; r*=*B; 
  #if(WYHASH_CONDOM>1)
  *A^=(uint64_t)r; *B^=(uint64_t)(r>>64);
  #else
  *A=(uint64_t)r; *B=(uint64_t)(r>>64);
  #endif
#elif defined(_MSC_VER) && defined(_M_X64)
  #if(WYHASH_CONDOM>1)
  uint64_t  a,  b;
  a=_umul128(*A,*B,&b);
  *A^=a;  *B^=b;
  #else
  *A=_umul128(*A,*B,B);
  #endif
#else
  uint64_t ha=*A>>32, hb=*B>>32, la=(uint32_t)*A, lb=(uint32_t)*B, hi, lo;
  uint64_t rh=ha*hb, rm0=ha*lb, rm1=hb*la, rl=la*lb, t=rl+(rm0<<32), c=t<rl;
  lo=t+(rm1<<32); c+=lo<t; hi=rh+(rm0>>32)+(rm1>>32)+c;
  #if(WYHASH_CONDOM>1)
  *A^=lo;  *B^=hi;
  #else
  *A=lo;  *B=hi;
  #endif
#endif
}

//multiply and xor mix function, aka MUM
static inline uint64_t _wymix(uint64_t A, uint64_t B){ _wymum(&A,&B); return A^B; }

//endian macros
#ifndef WYHASH_LITTLE_ENDIAN
  #if defined(_WIN32) || defined(__LITTLE_ENDIAN__) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
    #define WYHASH_LITTLE_ENDIAN 1
  #elif defined(__BIG_ENDIAN__) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
    #define WYHASH_LITTLE_ENDIAN 0
  #else
    #warning could not determine endianness! Falling back to little endian.
    #define WYHASH_LITTLE_ENDIAN 1
  #endif
#endif

//read functions
#if (WYHASH_LITTLE_ENDIAN)
static inline uint64_t _wyr8(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return v;}
static inline uint64_t _wyr4(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return v;}
#elif defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__)
static inline uint64_t _wyr8(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return __builtin_bswap64(v);}
static inline uint64_t _wyr4(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return __builtin_bswap32(v);}
#elif defined(_MSC_VER)
static inline uint64_t _wyr8(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return _byteswap_uint64(v);}
static inline uint64_t _wyr4(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return _byteswap_ulong(v);}
#else
static inline uint64_t _wyr8(const uint8_t *p) {
  uint64_t v; memcpy(&v, p, 8);
  return (((v >> 56) & 0xff)| ((v >> 40) & 0xff00)| ((v >> 24) & 0xff0000)| ((v >>  8) & 0xff000000)| ((v <<  8) & 0xff00000000)| ((v << 24) & 0xff0000000000)| ((v << 40) & 0xff000000000000)| ((v << 56) & 0xff00000000000000));
}
static inline uint64_t _wyr4(const uint8_t *p) {
  uint32_t v; memcpy(&v, p, 4);
  return (((v >> 24) & 0xff)| ((v >>  8) & 0xff00)| ((v <<  8) & 0xff0000)| ((v << 24) & 0xff000000));
}
#endif
static inline uint64_t _wyr3(const uint8_t *p, size_t k) { return (((uint64_t)p[0])<<16)|(((uint64_t)p[k>>1])<<8)|p[k-1];}
//wyhash main function
static inline uint64_t wyhash(const void *key, size_t len, uint64_t seed, const uint64_t *secret){
  const uint8_t *p=(const uint8_t *)key; seed^=_wymix(seed^secret[0],secret[1]);	uint64_t	a,	b;
  if(_likely_(len<=16)){
    if(_likely_(len>=4)){ a=(_wyr4(p)<<32)|_wyr4(p+((len>>3)<<2)); b=(_wyr4(p+len-4)<<32)|_wyr4(p+len-4-((len>>3)<<2)); }
    else if(_likely_(len>0)){ a=_wyr3(p,len); b=0;}
    else a=b=0;
  }
  else{
    size_t i=len; 
    if(_unlikely_(i>=48)){
      uint64_t see1=seed, see2=seed;
      do{
        seed=_wymix(_wyr8(p)^secret[1],_wyr8(p+8)^seed);
        see1=_wymix(_wyr8(p+16)^secret[2],_wyr8(p+24)^see1);
        see2=_wymix(_wyr8(p+32)^secret[3],_wyr8(p+40)^see2);
        p+=48; i-=48;
      }while(_likely_(i>=48));
      seed^=see1^see2;
    }
    while(_unlikely_(i>16)){  seed=_wymix(_wyr8(p)^secret[1],_wyr8(p+8)^seed);  i-=16; p+=16;  }
    a=_wyr8(p+i-16);  b=_wyr8(p+i-8);
  }
  a^=secret[1]; b^=seed;  _wymum(&a,&b);
  return  _wymix(a^secret[0]^len,b^secret[1]);
}

//the default secret parameters
static const uint64_t _wyp[4] = {0x2d358dccaa6c78a5ull, 0x8bb84b93962eacc9ull, 0x4b33a62ed433d4a3ull, 0x4d5a2da51de1aa47ull};

//a useful 64bit-64bit mix function to produce deterministic pseudo random numbers that can pass BigCrush and PractRand
static inline uint64_t wyhash64(uint64_t A, uint64_t B){ A^=0x2d358dccaa6c78a5ull; B^=0x8bb84b93962eacc9ull; _wymum(&A,&B); return _wymix(A^0x2d358dccaa6c78a5ull,B^0x8bb84b93962eacc9ull);}

//The wyrand PRNG that pass BigCrush and PractRand
static inline uint64_t wyrand(uint64_t *seed){ *seed+=0x2d358dccaa6c78a5ull; return _wymix(*seed,*seed^0x8bb84b93962eacc9ull);}

//convert any 64 bit pseudo random numbers to uniform distribution [0,1). It can be combined with wyrand, wyhash64 or wyhash.
static inline double wy2u01(uint64_t r){ const double _wynorm=1.0/(1ull<<52); return (r>>12)*_wynorm;}

//convert any 64 bit pseudo random numbers to APPROXIMATE Gaussian distribution. It can be combined with wyrand, wyhash64 or wyhash.
static inline double wy2gau(uint64_t r){ const double _wynorm=1.0/(1ull<<20); return ((r&0x1fffff)+((r>>21)&0x1fffff)+((r>>42)&0x1fffff))*_wynorm-3.0;}

#ifdef	WYTRNG
#include <sys/time.h>
//The wytrand true random number generator, passed BigCrush.
static inline uint64_t wytrand(uint64_t *seed){
	struct	timeval	t;	gettimeofday(&t,0);
	uint64_t	teed=(((uint64_t)t.tv_sec)<<32)|t.tv_usec;
	teed=_wymix(teed^_wyp[0],*seed^_wyp[1]);
	*seed=_wymix(teed^_wyp[0],_wyp[2]);
	return _wymix(*seed,*seed^_wyp[3]);
}
#endif

#if(!WYHASH_32BIT_MUM)
//fast range integer random number generation on [0,k) credit to Daniel Lemire. May not work when WYHASH_32BIT_MUM=1. It can be combined with wyrand, wyhash64 or wyhash.
static inline uint64_t wy2u0k(uint64_t r, uint64_t k){ _wymum(&r,&k); return k; }
#endif

// modified from https://github.com/going-digital/Prime64 
static	inline	unsigned long long	mul_mod(unsigned long long a, unsigned long long b, unsigned long long m) {
    unsigned long long r=0;
    while (b) {
        if (b & 1) {
            unsigned long long r2 = r + a;
            if (r2 < r) r2 -= m;
            r = r2 % m;
        }
        b >>= 1;
        if (b) {
            unsigned long long a2 = a + a;
            if (a2 < a) a2 -= m;
            a = a2 % m;
        }
    }
    return r;
}
static inline unsigned long long pow_mod(unsigned long long a, unsigned long long b, unsigned long long m) {
    unsigned long long r=1;
    while (b) {
        if (b&1) r=mul_mod(r,a,m);
        b>>=1;
        if (b) a=mul_mod(a,a,m);
    }
    return r;
}
unsigned sprp(unsigned long long n, unsigned long long a) {
    unsigned long long d=n-1;
    unsigned char s=0;
    while (!(d & 0xff)) { d>>=8; s+=8; }
    if (!(d & 0xf)) { d>>=4; s+=4; }
    if (!(d & 0x3)) { d>>=2; s+=2; }
    if (!(d & 0x1)) { d>>=1; s+=1; }
    unsigned long long b=pow_mod(a,d,n);
    if ((b==1) || (b==(n-1))) return 1;
    unsigned char r;
    for (r=1; r<s; r++) {
        b=mul_mod(b,b,n);
        if (b<=1) return 0;
        if (b==(n-1)) return 1;
    }
    return 0;
}
unsigned is_prime(unsigned long long n) {
    if (n<2||!(n&1)) return 0;
    if (n<4) return 1;
    if (!sprp(n,2)) return 0;
    if (n<2047) return 1;
    if (!sprp(n,3)) return 0;
    if (!sprp(n,5)) return 0;
    if (!sprp(n,7)) return 0;
    if (!sprp(n,11)) return 0;
    if (!sprp(n,13)) return 0;
    if (!sprp(n,17)) return 0;
    if (!sprp(n,19)) return 0;
    if (!sprp(n,23)) return 0;
    if (!sprp(n,29)) return 0;
    if (!sprp(n,31)) return 0;
    if (!sprp(n,37)) return 0;
    return 1;
}
//make your own secret
static inline void make_secret(uint64_t seed, uint64_t *secret){
  uint8_t c[] = {15, 23, 27, 29, 30, 39, 43, 45, 46, 51, 53, 54, 57, 58, 60, 71, 75, 77, 78, 83, 85, 86, 89, 90, 92, 99, 101, 102, 105, 106, 108, 113, 114, 116, 120, 135, 139, 141, 142, 147, 149, 150, 153, 154, 156, 163, 165, 166, 169, 170, 172, 177, 178, 180, 184, 195, 197, 198, 201, 202, 204, 209, 210, 212, 216, 225, 226, 228, 232, 240 };
  for(size_t i=0;i<4;i++){
    uint8_t ok;
    do{
      ok=1; secret[i]=0;
      for(size_t j=0;j<64;j+=8) secret[i]|=((uint64_t)c[wyrand(&seed)%sizeof(c)])<<j;
      if(secret[i]%2==0){ ok=0; continue; }      
      for(size_t j=0;j<i;j++) {
#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__)
        if(__builtin_popcountll(secret[j]^secret[i])!=32){ ok=0; break; }
#elif defined(_MSC_VER) && defined(_M_X64)
        if(_mm_popcnt_u64(secret[j]^secret[i])!=32){ ok=0; break; }
#else
        //manual popcount
        uint64_t x = secret[j]^secret[i];
        x -= (x >> 1) & 0x5555555555555555;
        x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333);
        x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0f;
        x = (x * 0x0101010101010101) >> 56;
        if(x!=32){ ok=0; break; }
#endif
      }
      if(ok&&!is_prime(secret[i]))	ok=0;
    }while(!ok);
  }
}

#endif

/* The Unlicense
This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <http://unlicense.org/>
*/

--- benchmark_other_languages/c/hash_functions/xxh3.h ---

/*
 * xxHash - Extremely Fast Hash algorithm
 * Development source file for `xxh3`
 * Copyright (C) 2019-2021 Yann Collet
 *
 * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above
 *      copyright notice, this list of conditions and the following disclaimer
 *      in the documentation and/or other materials provided with the
 *      distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * You can contact the author at:
 *   - xxHash homepage: https://www.xxhash.com
 *   - xxHash source repository: https://github.com/Cyan4973/xxHash
 */

/*
 * Note: This file used to host the source code of XXH3_* variants.
 * during the development period.
 * The source code is now properly integrated within xxhash.h.
 *
 * xxh3.h is no longer useful,
 * but it is still provided for compatibility with source code
 * which used to include it directly.
 *
 * Programs are now highly discouraged to include xxh3.h.
 * Include `xxhash.h` instead, which is the officially supported interface.
 *
 * In the future, xxh3.h will start to generate warnings, then errors,
 * then it will be removed from source package and from include directory.
 */

/* Simulate the same impact as including the old xxh3.h source file */

#define XXH_INLINE_ALL
#include "xxhash.h"

--- benchmark_other_languages/c/hash_functions/xxhash.c ---

/*
 * xxHash - Extremely Fast Hash algorithm
 * Copyright (C) 2012-2023 Yann Collet
 *
 * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above
 *      copyright notice, this list of conditions and the following disclaimer
 *      in the documentation and/or other materials provided with the
 *      distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * You can contact the author at:
 *   - xxHash homepage: https://www.xxhash.com
 *   - xxHash source repository: https://github.com/Cyan4973/xxHash
 */

/*
 * xxhash.c instantiates functions defined in xxhash.h
 */

#define XXH_STATIC_LINKING_ONLY /* access advanced declarations */
#define XXH_IMPLEMENTATION      /* access definitions */

#include "xxhash.h"

--- benchmark_other_languages/c/hash_functions/xxhash.h ---

/*
 * xxHash - Extremely Fast Hash algorithm
 * Header File
 * Copyright (C) 2012-2023 Yann Collet
 *
 * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above
 *      copyright notice, this list of conditions and the following disclaimer
 *      in the documentation and/or other materials provided with the
 *      distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * You can contact the author at:
 *   - xxHash homepage: https://www.xxhash.com
 *   - xxHash source repository: https://github.com/Cyan4973/xxHash
 */

/*!
 * @mainpage xxHash
 *
 * xxHash is an extremely fast non-cryptographic hash algorithm, working at RAM speed
 * limits.
 *
 * It is proposed in four flavors, in three families:
 * 1. @ref XXH32_family
 *   - Classic 32-bit hash function. Simple, compact, and runs on almost all
 *     32-bit and 64-bit systems.
 * 2. @ref XXH64_family
 *   - Classic 64-bit adaptation of XXH32. Just as simple, and runs well on most
 *     64-bit systems (but _not_ 32-bit systems).
 * 3. @ref XXH3_family
 *   - Modern 64-bit and 128-bit hash function family which features improved
 *     strength and performance across the board, especially on smaller data.
 *     It benefits greatly from SIMD and 64-bit without requiring it.
 *
 * Benchmarks
 * ---
 * The reference system uses an Intel i7-9700K CPU, and runs Ubuntu x64 20.04.
 * The open source benchmark program is compiled with clang v10.0 using -O3 flag.
 *
 * | Hash Name            | ISA ext | Width | Large Data Speed | Small Data Velocity |
 * | -------------------- | ------- | ----: | ---------------: | ------------------: |
 * | XXH3_64bits()        | @b AVX2 |    64 |        59.4 GB/s |               133.1 |
 * | MeowHash             | AES-NI  |   128 |        58.2 GB/s |                52.5 |
 * | XXH3_128bits()       | @b AVX2 |   128 |        57.9 GB/s |               118.1 |
 * | CLHash               | PCLMUL  |    64 |        37.1 GB/s |                58.1 |
 * | XXH3_64bits()        | @b SSE2 |    64 |        31.5 GB/s |               133.1 |
 * | XXH3_128bits()       | @b SSE2 |   128 |        29.6 GB/s |               118.1 |
 * | RAM sequential read  |         |   N/A |        28.0 GB/s |                 N/A |
 * | ahash                | AES-NI  |    64 |        22.5 GB/s |               107.2 |
 * | City64               |         |    64 |        22.0 GB/s |                76.6 |
 * | T1ha2                |         |    64 |        22.0 GB/s |                99.0 |
 * | City128              |         |   128 |        21.7 GB/s |                57.7 |
 * | FarmHash             | AES-NI  |    64 |        21.3 GB/s |                71.9 |
 * | XXH64()              |         |    64 |        19.4 GB/s |                71.0 |
 * | SpookyHash           |         |    64 |        19.3 GB/s |                53.2 |
 * | Mum                  |         |    64 |        18.0 GB/s |                67.0 |
 * | CRC32C               | SSE4.2  |    32 |        13.0 GB/s |                57.9 |
 * | XXH32()              |         |    32 |         9.7 GB/s |                71.9 |
 * | City32               |         |    32 |         9.1 GB/s |                66.0 |
 * | Blake3*              | @b AVX2 |   256 |         4.4 GB/s |                 8.1 |
 * | Murmur3              |         |    32 |         3.9 GB/s |                56.1 |
 * | SipHash*             |         |    64 |         3.0 GB/s |                43.2 |
 * | Blake3*              | @b SSE2 |   256 |         2.4 GB/s |                 8.1 |
 * | HighwayHash          |         |    64 |         1.4 GB/s |                 6.0 |
 * | FNV64                |         |    64 |         1.2 GB/s |                62.7 |
 * | Blake2*              |         |   256 |         1.1 GB/s |                 5.1 |
 * | SHA1*                |         |   160 |         0.8 GB/s |                 5.6 |
 * | MD5*                 |         |   128 |         0.6 GB/s |                 7.8 |
 * @note
 *   - Hashes which require a specific ISA extension are noted. SSE2 is also noted,
 *     even though it is mandatory on x64.
 *   - Hashes with an asterisk are cryptographic. Note that MD5 is non-cryptographic
 *     by modern standards.
 *   - Small data velocity is a rough average of algorithm's efficiency for small
 *     data. For more accurate information, see the wiki.
 *   - More benchmarks and strength tests are found on the wiki:
 *         https://github.com/Cyan4973/xxHash/wiki
 *
 * Usage
 * ------
 * All xxHash variants use a similar API. Changing the algorithm is a trivial
 * substitution.
 *
 * @pre
 *    For functions which take an input and length parameter, the following
 *    requirements are assumed:
 *    - The range from [`input`, `input + length`) is valid, readable memory.
 *      - The only exception is if the `length` is `0`, `input` may be `NULL`.
 *    - For C++, the objects must have the *TriviallyCopyable* property, as the
 *      functions access bytes directly as if it was an array of `unsigned char`.
 *
 * @anchor single_shot_example
 * **Single Shot**
 *
 * These functions are stateless functions which hash a contiguous block of memory,
 * immediately returning the result. They are the easiest and usually the fastest
 * option.
 *
 * XXH32(), XXH64(), XXH3_64bits(), XXH3_128bits()
 *
 * @code{.c}
 *   #include <string.h>
 *   #include "xxhash.h"
 *
 *   // Example for a function which hashes a null terminated string with XXH32().
 *   XXH32_hash_t hash_string(const char* string, XXH32_hash_t seed)
 *   {
 *       // NULL pointers are only valid if the length is zero
 *       size_t length = (string == NULL) ? 0 : strlen(string);
 *       return XXH32(string, length, seed);
 *   }
 * @endcode
 *
 *
 * @anchor streaming_example
 * **Streaming**
 *
 * These groups of functions allow incremental hashing of unknown size, even
 * more than what would fit in a size_t.
 *
 * XXH32_reset(), XXH64_reset(), XXH3_64bits_reset(), XXH3_128bits_reset()
 *
 * @code{.c}
 *   #include <stdio.h>
 *   #include <assert.h>
 *   #include "xxhash.h"
 *   // Example for a function which hashes a FILE incrementally with XXH3_64bits().
 *   XXH64_hash_t hashFile(FILE* f)
 *   {
 *       // Allocate a state struct. Do not just use malloc() or new.
 *       XXH3_state_t* state = XXH3_createState();
 *       assert(state != NULL && "Out of memory!");
 *       // Reset the state to start a new hashing session.
 *       XXH3_64bits_reset(state);
 *       char buffer[4096];
 *       size_t count;
 *       // Read the file in chunks
 *       while ((count = fread(buffer, 1, sizeof(buffer), f)) != 0) {
 *           // Run update() as many times as necessary to process the data
 *           XXH3_64bits_update(state, buffer, count);
 *       }
 *       // Retrieve the finalized hash. This will not change the state.
 *       XXH64_hash_t result = XXH3_64bits_digest(state);
 *       // Free the state. Do not use free().
 *       XXH3_freeState(state);
 *       return result;
 *   }
 * @endcode
 *
 * Streaming functions generate the xxHash value from an incremental input.
 * This method is slower than single-call functions, due to state management.
 * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized.
 *
 * An XXH state must first be allocated using `XXH*_createState()`.
 *
 * Start a new hash by initializing the state with a seed using `XXH*_reset()`.
 *
 * Then, feed the hash state by calling `XXH*_update()` as many times as necessary.
 *
 * The function returns an error code, with 0 meaning OK, and any other value
 * meaning there is an error.
 *
 * Finally, a hash value can be produced anytime, by using `XXH*_digest()`.
 * This function returns the nn-bits hash as an int or long long.
 *
 * It's still possible to continue inserting input into the hash state after a
 * digest, and generate new hash values later on by invoking `XXH*_digest()`.
 *
 * When done, release the state using `XXH*_freeState()`.
 *
 *
 * @anchor canonical_representation_example
 * **Canonical Representation**
 *
 * The default return values from XXH functions are unsigned 32, 64 and 128 bit
 * integers.
 * This the simplest and fastest format for further post-processing.
 *
 * However, this leaves open the question of what is the order on the byte level,
 * since little and big endian conventions will store the same number differently.
 *
 * The canonical representation settles this issue by mandating big-endian
 * convention, the same convention as human-readable numbers (large digits first).
 *
 * When writing hash values to storage, sending them over a network, or printing
 * them, it's highly recommended to use the canonical representation to ensure
 * portability across a wider range of systems, present and future.
 *
 * The following functions allow transformation of hash values to and from
 * canonical format.
 *
 * XXH32_canonicalFromHash(), XXH32_hashFromCanonical(),
 * XXH64_canonicalFromHash(), XXH64_hashFromCanonical(),
 * XXH128_canonicalFromHash(), XXH128_hashFromCanonical(),
 *
 * @code{.c}
 *   #include <stdio.h>
 *   #include "xxhash.h"
 *
 *   // Example for a function which prints XXH32_hash_t in human readable format
 *   void printXxh32(XXH32_hash_t hash)
 *   {
 *       XXH32_canonical_t cano;
 *       XXH32_canonicalFromHash(&cano, hash);
 *       size_t i;
 *       for(i = 0; i < sizeof(cano.digest); ++i) {
 *           printf("%02x", cano.digest[i]);
 *       }
 *       printf("\n");
 *   }
 *
 *   // Example for a function which converts XXH32_canonical_t to XXH32_hash_t
 *   XXH32_hash_t convertCanonicalToXxh32(XXH32_canonical_t cano)
 *   {
 *       XXH32_hash_t hash = XXH32_hashFromCanonical(&cano);
 *       return hash;
 *   }
 * @endcode
 *
 *
 * @file xxhash.h
 * xxHash prototypes and implementation
 */

#if defined (__cplusplus)
extern "C" {
#endif

/* ****************************
 *  INLINE mode
 ******************************/
/*!
 * @defgroup public Public API
 * Contains details on the public xxHash functions.
 * @{
 */
#ifdef XXH_DOXYGEN
/*!
 * @brief Gives access to internal state declaration, required for static allocation.
 *
 * Incompatible with dynamic linking, due to risks of ABI changes.
 *
 * Usage:
 * @code{.c}
 *     #define XXH_STATIC_LINKING_ONLY
 *     #include "xxhash.h"
 * @endcode
 */
#  define XXH_STATIC_LINKING_ONLY
/* Do not undef XXH_STATIC_LINKING_ONLY for Doxygen */

/*!
 * @brief Gives access to internal definitions.
 *
 * Usage:
 * @code{.c}
 *     #define XXH_STATIC_LINKING_ONLY
 *     #define XXH_IMPLEMENTATION
 *     #include "xxhash.h"
 * @endcode
 */
#  define XXH_IMPLEMENTATION
/* Do not undef XXH_IMPLEMENTATION for Doxygen */

/*!
 * @brief Exposes the implementation and marks all functions as `inline`.
 *
 * Use these build macros to inline xxhash into the target unit.
 * Inlining improves performance on small inputs, especially when the length is
 * expressed as a compile-time constant:
 *
 *  https://fastcompression.blogspot.com/2018/03/xxhash-for-small-keys-impressive-power.html
 *
 * It also keeps xxHash symbols private to the unit, so they are not exported.
 *
 * Usage:
 * @code{.c}
 *     #define XXH_INLINE_ALL
 *     #include "xxhash.h"
 * @endcode
 * Do not compile and link xxhash.o as a separate object, as it is not useful.
 */
#  define XXH_INLINE_ALL
#  undef XXH_INLINE_ALL
/*!
 * @brief Exposes the implementation without marking functions as inline.
 */
#  define XXH_PRIVATE_API
#  undef XXH_PRIVATE_API
/*!
 * @brief Emulate a namespace by transparently prefixing all symbols.
 *
 * If you want to include _and expose_ xxHash functions from within your own
 * library, but also want to avoid symbol collisions with other libraries which
 * may also include xxHash, you can use @ref XXH_NAMESPACE to automatically prefix
 * any public symbol from xxhash library with the value of @ref XXH_NAMESPACE
 * (therefore, avoid empty or numeric values).
 *
 * Note that no change is required within the calling program as long as it
 * includes `xxhash.h`: Regular symbol names will be automatically translated
 * by this header.
 */
#  define XXH_NAMESPACE /* YOUR NAME HERE */
#  undef XXH_NAMESPACE
#endif

#if (defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)) \
    && !defined(XXH_INLINE_ALL_31684351384)
   /* this section should be traversed only once */
#  define XXH_INLINE_ALL_31684351384
   /* give access to the advanced API, required to compile implementations */
#  undef XXH_STATIC_LINKING_ONLY   /* avoid macro redef */
#  define XXH_STATIC_LINKING_ONLY
   /* make all functions private */
#  undef XXH_PUBLIC_API
#  if defined(__GNUC__)
#    define XXH_PUBLIC_API static __inline __attribute__((unused))
#  elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
#    define XXH_PUBLIC_API static inline
#  elif defined(_MSC_VER)
#    define XXH_PUBLIC_API static __inline
#  else
     /* note: this version may generate warnings for unused static functions */
#    define XXH_PUBLIC_API static
#  endif

   /*
    * This part deals with the special case where a unit wants to inline xxHash,
    * but "xxhash.h" has previously been included without XXH_INLINE_ALL,
    * such as part of some previously included *.h header file.
    * Without further action, the new include would just be ignored,
    * and functions would effectively _not_ be inlined (silent failure).
    * The following macros solve this situation by prefixing all inlined names,
    * avoiding naming collision with previous inclusions.
    */
   /* Before that, we unconditionally #undef all symbols,
    * in case they were already defined with XXH_NAMESPACE.
    * They will then be redefined for XXH_INLINE_ALL
    */
#  undef XXH_versionNumber
    /* XXH32 */
#  undef XXH32
#  undef XXH32_createState
#  undef XXH32_freeState
#  undef XXH32_reset
#  undef XXH32_update
#  undef XXH32_digest
#  undef XXH32_copyState
#  undef XXH32_canonicalFromHash
#  undef XXH32_hashFromCanonical
    /* XXH64 */
#  undef XXH64
#  undef XXH64_createState
#  undef XXH64_freeState
#  undef XXH64_reset
#  undef XXH64_update
#  undef XXH64_digest
#  undef XXH64_copyState
#  undef XXH64_canonicalFromHash
#  undef XXH64_hashFromCanonical
    /* XXH3_64bits */
#  undef XXH3_64bits
#  undef XXH3_64bits_withSecret
#  undef XXH3_64bits_withSeed
#  undef XXH3_64bits_withSecretandSeed
#  undef XXH3_createState
#  undef XXH3_freeState
#  undef XXH3_copyState
#  undef XXH3_64bits_reset
#  undef XXH3_64bits_reset_withSeed
#  undef XXH3_64bits_reset_withSecret
#  undef XXH3_64bits_update
#  undef XXH3_64bits_digest
#  undef XXH3_generateSecret
    /* XXH3_128bits */
#  undef XXH128
#  undef XXH3_128bits
#  undef XXH3_128bits_withSeed
#  undef XXH3_128bits_withSecret
#  undef XXH3_128bits_reset
#  undef XXH3_128bits_reset_withSeed
#  undef XXH3_128bits_reset_withSecret
#  undef XXH3_128bits_reset_withSecretandSeed
#  undef XXH3_128bits_update
#  undef XXH3_128bits_digest
#  undef XXH128_isEqual
#  undef XXH128_cmp
#  undef XXH128_canonicalFromHash
#  undef XXH128_hashFromCanonical
    /* Finally, free the namespace itself */
#  undef XXH_NAMESPACE

    /* employ the namespace for XXH_INLINE_ALL */
#  define XXH_NAMESPACE XXH_INLINE_
   /*
    * Some identifiers (enums, type names) are not symbols,
    * but they must nonetheless be renamed to avoid redeclaration.
    * Alternative solution: do not redeclare them.
    * However, this requires some #ifdefs, and has a more dispersed impact.
    * Meanwhile, renaming can be achieved in a single place.
    */
#  define XXH_IPREF(Id)   XXH_NAMESPACE ## Id
#  define XXH_OK XXH_IPREF(XXH_OK)
#  define XXH_ERROR XXH_IPREF(XXH_ERROR)
#  define XXH_errorcode XXH_IPREF(XXH_errorcode)
#  define XXH32_canonical_t  XXH_IPREF(XXH32_canonical_t)
#  define XXH64_canonical_t  XXH_IPREF(XXH64_canonical_t)
#  define XXH128_canonical_t XXH_IPREF(XXH128_canonical_t)
#  define XXH32_state_s XXH_IPREF(XXH32_state_s)
#  define XXH32_state_t XXH_IPREF(XXH32_state_t)
#  define XXH64_state_s XXH_IPREF(XXH64_state_s)
#  define XXH64_state_t XXH_IPREF(XXH64_state_t)
#  define XXH3_state_s  XXH_IPREF(XXH3_state_s)
#  define XXH3_state_t  XXH_IPREF(XXH3_state_t)
#  define XXH128_hash_t XXH_IPREF(XXH128_hash_t)
   /* Ensure the header is parsed again, even if it was previously included */
#  undef XXHASH_H_5627135585666179
#  undef XXHASH_H_STATIC_13879238742
#endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */

/* ****************************************************************
 *  Stable API
 *****************************************************************/
#ifndef XXHASH_H_5627135585666179
#define XXHASH_H_5627135585666179 1

/*! @brief Marks a global symbol. */
#if !defined(XXH_INLINE_ALL) && !defined(XXH_PRIVATE_API)
#  if defined(WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT))
#    ifdef XXH_EXPORT
#      define XXH_PUBLIC_API __declspec(dllexport)
#    elif XXH_IMPORT
#      define XXH_PUBLIC_API __declspec(dllimport)
#    endif
#  else
#    define XXH_PUBLIC_API   /* do nothing */
#  endif
#endif

#ifdef XXH_NAMESPACE
#  define XXH_CAT(A,B) A##B
#  define XXH_NAME2(A,B) XXH_CAT(A,B)
#  define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber)
/* XXH32 */
#  define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32)
#  define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState)
#  define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState)
#  define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset)
#  define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update)
#  define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest)
#  define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState)
#  define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash)
#  define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical)
/* XXH64 */
#  define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64)
#  define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState)
#  define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState)
#  define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset)
#  define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update)
#  define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest)
#  define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState)
#  define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash)
#  define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical)
/* XXH3_64bits */
#  define XXH3_64bits XXH_NAME2(XXH_NAMESPACE, XXH3_64bits)
#  define XXH3_64bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecret)
#  define XXH3_64bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSeed)
#  define XXH3_64bits_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecretandSeed)
#  define XXH3_createState XXH_NAME2(XXH_NAMESPACE, XXH3_createState)
#  define XXH3_freeState XXH_NAME2(XXH_NAMESPACE, XXH3_freeState)
#  define XXH3_copyState XXH_NAME2(XXH_NAMESPACE, XXH3_copyState)
#  define XXH3_64bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset)
#  define XXH3_64bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSeed)
#  define XXH3_64bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecret)
#  define XXH3_64bits_reset_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecretandSeed)
#  define XXH3_64bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_update)
#  define XXH3_64bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_digest)
#  define XXH3_generateSecret XXH_NAME2(XXH_NAMESPACE, XXH3_generateSecret)
#  define XXH3_generateSecret_fromSeed XXH_NAME2(XXH_NAMESPACE, XXH3_generateSecret_fromSeed)
/* XXH3_128bits */
#  define XXH128 XXH_NAME2(XXH_NAMESPACE, XXH128)
#  define XXH3_128bits XXH_NAME2(XXH_NAMESPACE, XXH3_128bits)
#  define XXH3_128bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSeed)
#  define XXH3_128bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecret)
#  define XXH3_128bits_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecretandSeed)
#  define XXH3_128bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset)
#  define XXH3_128bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSeed)
#  define XXH3_128bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecret)
#  define XXH3_128bits_reset_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecretandSeed)
#  define XXH3_128bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_update)
#  define XXH3_128bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_digest)
#  define XXH128_isEqual XXH_NAME2(XXH_NAMESPACE, XXH128_isEqual)
#  define XXH128_cmp     XXH_NAME2(XXH_NAMESPACE, XXH128_cmp)
#  define XXH128_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH128_canonicalFromHash)
#  define XXH128_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH128_hashFromCanonical)
#endif


/* *************************************
*  Compiler specifics
***************************************/

/* specific declaration modes for Windows */
#if !defined(XXH_INLINE_ALL) && !defined(XXH_PRIVATE_API)
#  if defined(WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT))
#    ifdef XXH_EXPORT
#      define XXH_PUBLIC_API __declspec(dllexport)
#    elif XXH_IMPORT
#      define XXH_PUBLIC_API __declspec(dllimport)
#    endif
#  else
#    define XXH_PUBLIC_API   /* do nothing */
#  endif
#endif

#if defined (__GNUC__)
# define XXH_CONSTF  __attribute__((const))
# define XXH_PUREF   __attribute__((pure))
# define XXH_MALLOCF __attribute__((malloc))
#else
# define XXH_CONSTF  /* disable */
# define XXH_PUREF
# define XXH_MALLOCF
#endif

/* *************************************
*  Version
***************************************/
#define XXH_VERSION_MAJOR    0
#define XXH_VERSION_MINOR    8
#define XXH_VERSION_RELEASE  2
/*! @brief Version number, encoded as two digits each */
#define XXH_VERSION_NUMBER  (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE)

/*!
 * @brief Obtains the xxHash version.
 *
 * This is mostly useful when xxHash is compiled as a shared library,
 * since the returned value comes from the library, as opposed to header file.
 *
 * @return @ref XXH_VERSION_NUMBER of the invoked library.
 */
XXH_PUBLIC_API XXH_CONSTF unsigned XXH_versionNumber (void);


/* ****************************
*  Common basic types
******************************/
#include <stddef.h>   /* size_t */
/*!
 * @brief Exit code for the streaming API.
 */
typedef enum {
    XXH_OK = 0, /*!< OK */
    XXH_ERROR   /*!< Error */
} XXH_errorcode;


/*-**********************************************************************
*  32-bit hash
************************************************************************/
#if defined(XXH_DOXYGEN) /* Don't show <stdint.h> include */
/*!
 * @brief An unsigned 32-bit integer.
 *
 * Not necessarily defined to `uint32_t` but functionally equivalent.
 */
typedef uint32_t XXH32_hash_t;

#elif !defined (__VMS) \
  && (defined (__cplusplus) \
  || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
#   include <stdint.h>
    typedef uint32_t XXH32_hash_t;

#else
#   include <limits.h>
#   if UINT_MAX == 0xFFFFFFFFUL
      typedef unsigned int XXH32_hash_t;
#   elif ULONG_MAX == 0xFFFFFFFFUL
      typedef unsigned long XXH32_hash_t;
#   else
#     error "unsupported platform: need a 32-bit type"
#   endif
#endif

/*!
 * @}
 *
 * @defgroup XXH32_family XXH32 family
 * @ingroup public
 * Contains functions used in the classic 32-bit xxHash algorithm.
 *
 * @note
 *   XXH32 is useful for older platforms, with no or poor 64-bit performance.
 *   Note that the @ref XXH3_family provides competitive speed for both 32-bit
 *   and 64-bit systems, and offers true 64/128 bit hash results.
 *
 * @see @ref XXH64_family, @ref XXH3_family : Other xxHash families
 * @see @ref XXH32_impl for implementation details
 * @{
 */

/*!
 * @brief Calculates the 32-bit hash of @p input using xxHash32.
 *
 * @param input The block of data to be hashed, at least @p length bytes in size.
 * @param length The length of @p input, in bytes.
 * @param seed The 32-bit seed to alter the hash's output predictably.
 *
 * @pre
 *   The memory between @p input and @p input + @p length must be valid,
 *   readable, contiguous memory. However, if @p length is `0`, @p input may be
 *   `NULL`. In C++, this also must be *TriviallyCopyable*.
 *
 * @return The calculated 32-bit xxHash32 value.
 *
 * @see @ref single_shot_example "Single Shot Example" for an example.
 */
XXH_PUBLIC_API XXH_PUREF XXH32_hash_t XXH32 (const void* input, size_t length, XXH32_hash_t seed);

#ifndef XXH_NO_STREAM
/*!
 * @typedef struct XXH32_state_s XXH32_state_t
 * @brief The opaque state struct for the XXH32 streaming API.
 *
 * @see XXH32_state_s for details.
 * @see @ref streaming_example "Streaming Example"
 */
typedef struct XXH32_state_s XXH32_state_t;

/*!
 * @brief Allocates an @ref XXH32_state_t.
 *
 * @return An allocated pointer of @ref XXH32_state_t on success.
 * @return `NULL` on failure.
 *
 * @note Must be freed with XXH32_freeState().
 *
 * @see @ref streaming_example "Streaming Example"
 */
XXH_PUBLIC_API XXH_MALLOCF XXH32_state_t* XXH32_createState(void);
/*!
 * @brief Frees an @ref XXH32_state_t.
 *
 * @param statePtr A pointer to an @ref XXH32_state_t allocated with @ref XXH32_createState().
 *
 * @return @ref XXH_OK.
 *
 * @note @p statePtr must be allocated with XXH32_createState().
 *
 * @see @ref streaming_example "Streaming Example"
 *
 */
XXH_PUBLIC_API XXH_errorcode  XXH32_freeState(XXH32_state_t* statePtr);
/*!
 * @brief Copies one @ref XXH32_state_t to another.
 *
 * @param dst_state The state to copy to.
 * @param src_state The state to copy from.
 * @pre
 *   @p dst_state and @p src_state must not be `NULL` and must not overlap.
 */
XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state);

/*!
 * @brief Resets an @ref XXH32_state_t to begin a new hash.
 *
 * @param statePtr The state struct to reset.
 * @param seed The 32-bit seed to alter the hash result predictably.
 *
 * @pre
 *   @p statePtr must not be `NULL`.
 *
 * @return @ref XXH_OK on success.
 * @return @ref XXH_ERROR on failure.
 *
 * @note This function resets and seeds a state. Call it before @ref XXH32_update().
 *
 * @see @ref streaming_example "Streaming Example"
 */
XXH_PUBLIC_API XXH_errorcode XXH32_reset  (XXH32_state_t* statePtr, XXH32_hash_t seed);

/*!
 * @brief Consumes a block of @p input to an @ref XXH32_state_t.
 *
 * @param statePtr The state struct to update.
 * @param input The block of data to be hashed, at least @p length bytes in size.
 * @param length The length of @p input, in bytes.
 *
 * @pre
 *   @p statePtr must not be `NULL`.
 * @pre
 *   The memory between @p input and @p input + @p length must be valid,
 *   readable, contiguous memory. However, if @p length is `0`, @p input may be
 *   `NULL`. In C++, this also must be *TriviallyCopyable*.
 *
 * @return @ref XXH_OK on success.
 * @return @ref XXH_ERROR on failure.
 *
 * @note Call this to incrementally consume blocks of data.
 *
 * @see @ref streaming_example "Streaming Example"
 */
XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length);

/*!
 * @brief Returns the calculated hash value from an @ref XXH32_state_t.
 *
 * @param statePtr The state struct to calculate the hash from.
 *
 * @pre
 *  @p statePtr must not be `NULL`.
 *
 * @return The calculated 32-bit xxHash32 value from that state.
 *
 * @note
 *   Calling XXH32_digest() will not affect @p statePtr, so you can update,
 *   digest, and update again.
 *
 * @see @ref streaming_example "Streaming Example"
 */
XXH_PUBLIC_API XXH_PUREF XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr);
#endif /* !XXH_NO_STREAM */

/*******   Canonical representation   *******/

/*!
 * @brief Canonical (big endian) representation of @ref XXH32_hash_t.
 */
typedef struct {
    unsigned char digest[4]; /*!< Hash bytes, big endian */
} XXH32_canonical_t;

/*!
 * @brief Converts an @ref XXH32_hash_t to a big endian @ref XXH32_canonical_t.
 *
 * @param dst  The @ref XXH32_canonical_t pointer to be stored to.
 * @param hash The @ref XXH32_hash_t to be converted.
 *
 * @pre
 *   @p dst must not be `NULL`.
 *
 * @see @ref canonical_representation_example "Canonical Representation Example"
 */
XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash);

/*!
 * @brief Converts an @ref XXH32_canonical_t to a native @ref XXH32_hash_t.
 *
 * @param src The @ref XXH32_canonical_t to convert.
 *
 * @pre
 *   @p src must not be `NULL`.
 *
 * @return The converted hash.
 *
 * @see @ref canonical_representation_example "Canonical Representation Example"
 */
XXH_PUBLIC_API XXH_PUREF XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src);


/*! @cond Doxygen ignores this part */
#ifdef __has_attribute
# define XXH_HAS_ATTRIBUTE(x) __has_attribute(x)
#else
# define XXH_HAS_ATTRIBUTE(x) 0
#endif
/*! @endcond */

/*! @cond Doxygen ignores this part */
/*
 * C23 __STDC_VERSION__ number hasn't been specified yet. For now
 * leave as `201711L` (C17 + 1).
 * TODO: Update to correct value when its been specified.
 */
#define XXH_C23_VN 201711L
/*! @endcond */

/*! @cond Doxygen ignores this part */
/* C-language Attributes are added in C23. */
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= XXH_C23_VN) && defined(__has_c_attribute)
# define XXH_HAS_C_ATTRIBUTE(x) __has_c_attribute(x)
#else
# define XXH_HAS_C_ATTRIBUTE(x) 0
#endif
/*! @endcond */

/*! @cond Doxygen ignores this part */
#if defined(__cplusplus) && defined(__has_cpp_attribute)
# define XXH_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
#else
# define XXH_HAS_CPP_ATTRIBUTE(x) 0
#endif
/*! @endcond */

/*! @cond Doxygen ignores this part */
/*
 * Define XXH_FALLTHROUGH macro for annotating switch case with the 'fallthrough' attribute
 * introduced in CPP17 and C23.
 * CPP17 : https://en.cppreference.com/w/cpp/language/attributes/fallthrough
 * C23   : https://en.cppreference.com/w/c/language/attributes/fallthrough
 */
#if XXH_HAS_C_ATTRIBUTE(fallthrough) || XXH_HAS_CPP_ATTRIBUTE(fallthrough)
# define XXH_FALLTHROUGH [[fallthrough]]
#elif XXH_HAS_ATTRIBUTE(__fallthrough__)
# define XXH_FALLTHROUGH __attribute__ ((__fallthrough__))
#else
# define XXH_FALLTHROUGH /* fallthrough */
#endif
/*! @endcond */

/*! @cond Doxygen ignores this part */
/*
 * Define XXH_NOESCAPE for annotated pointers in public API.
 * https://clang.llvm.org/docs/AttributeReference.html#noescape
 * As of writing this, only supported by clang.
 */
#if XXH_HAS_ATTRIBUTE(noescape)
# define XXH_NOESCAPE __attribute__((noescape))
#else
# define XXH_NOESCAPE
#endif
/*! @endcond */


/*!
 * @}
 * @ingroup public
 * @{
 */

#ifndef XXH_NO_LONG_LONG
/*-**********************************************************************
*  64-bit hash
************************************************************************/
#if defined(XXH_DOXYGEN) /* don't include <stdint.h> */
/*!
 * @brief An unsigned 64-bit integer.
 *
 * Not necessarily defined to `uint64_t` but functionally equivalent.
 */
typedef uint64_t XXH64_hash_t;
#elif !defined (__VMS) \
  && (defined (__cplusplus) \
  || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
#  include <stdint.h>
   typedef uint64_t XXH64_hash_t;
#else
#  include <limits.h>
#  if defined(__LP64__) && ULONG_MAX == 0xFFFFFFFFFFFFFFFFULL
     /* LP64 ABI says uint64_t is unsigned long */
     typedef unsigned long XXH64_hash_t;
#  else
     /* the following type must have a width of 64-bit */
     typedef unsigned long long XXH64_hash_t;
#  endif
#endif

/*!
 * @}
 *
 * @defgroup XXH64_family XXH64 family
 * @ingroup public
 * @{
 * Contains functions used in the classic 64-bit xxHash algorithm.
 *
 * @note
 *   XXH3 provides competitive speed for both 32-bit and 64-bit systems,
 *   and offers true 64/128 bit hash results.
 *   It provides better speed for systems with vector processing capabilities.
 */

/*!
 * @brief Calculates the 64-bit hash of @p input using xxHash64.
 *
 * @param input The block of data to be hashed, at least @p length bytes in size.
 * @param length The length of @p input, in bytes.
 * @param seed The 64-bit seed to alter the hash's output predictably.
 *
 * @pre
 *   The memory between @p input and @p input + @p length must be valid,
 *   readable, contiguous memory. However, if @p length is `0`, @p input may be
 *   `NULL`. In C++, this also must be *TriviallyCopyable*.
 *
 * @return The calculated 64-bit xxHash64 value.
 *
 * @see @ref single_shot_example "Single Shot Example" for an example.
 */
XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH64(XXH_NOESCAPE const void* input, size_t length, XXH64_hash_t seed);

/*******   Streaming   *******/
#ifndef XXH_NO_STREAM
/*!
 * @brief The opaque state struct for the XXH64 streaming API.
 *
 * @see XXH64_state_s for details.
 * @see @ref streaming_example "Streaming Example"
 */
typedef struct XXH64_state_s XXH64_state_t;   /* incomplete type */

/*!
 * @brief Allocates an @ref XXH64_state_t.
 *
 * @return An allocated pointer of @ref XXH64_state_t on success.
 * @return `NULL` on failure.
 *
 * @note Must be freed with XXH64_freeState().
 *
 * @see @ref streaming_example "Streaming Example"
 */
XXH_PUBLIC_API XXH_MALLOCF XXH64_state_t* XXH64_createState(void);

/*!
 * @brief Frees an @ref XXH64_state_t.
 *
 * @param statePtr A pointer to an @ref XXH64_state_t allocated with @ref XXH64_createState().
 *
 * @return @ref XXH_OK.
 *
 * @note @p statePtr must be allocated with XXH64_createState().
 *
 * @see @ref streaming_example "Streaming Example"
 */
XXH_PUBLIC_API XXH_errorcode  XXH64_freeState(XXH64_state_t* statePtr);

/*!
 * @brief Copies one @ref XXH64_state_t to another.
 *
 * @param dst_state The state to copy to.
 * @param src_state The state to copy from.
 * @pre
 *   @p dst_state and @p src_state must not be `NULL` and must not overlap.
 */
XXH_PUBLIC_API void XXH64_copyState(XXH_NOESCAPE XXH64_state_t* dst_state, const XXH64_state_t* src_state);

/*!
 * @brief Resets an @ref XXH64_state_t to begin a new hash.
 *
 * @param statePtr The state struct to reset.
 * @param seed The 64-bit seed to alter the hash result predictably.
 *
 * @pre
 *   @p statePtr must not be `NULL`.
 *
 * @return @ref XXH_OK on success.
 * @return @ref XXH_ERROR on failure.
 *
 * @note This function resets and seeds a state. Call it before @ref XXH64_update().
 *
 * @see @ref streaming_example "Streaming Example"
 */
XXH_PUBLIC_API XXH_errorcode XXH64_reset  (XXH_NOESCAPE XXH64_state_t* statePtr, XXH64_hash_t seed);

/*!
 * @brief Consumes a block of @p input to an @ref XXH64_state_t.
 *
 * @param statePtr The state struct to update.
 * @param input The block of data to be hashed, at least @p length bytes in size.
 * @param length The length of @p input, in bytes.
 *
 * @pre
 *   @p statePtr must not be `NULL`.
 * @pre
 *   The memory between @p input and @p input + @p length must be valid,
 *   readable, contiguous memory. However, if @p length is `0`, @p input may be
 *   `NULL`. In C++, this also must be *TriviallyCopyable*.
 *
 * @return @ref XXH_OK on success.
 * @return @ref XXH_ERROR on failure.
 *
 * @note Call this to incrementally consume blocks of data.
 *
 * @see @ref streaming_example "Streaming Example"
 */
XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH_NOESCAPE XXH64_state_t* statePtr, XXH_NOESCAPE const void* input, size_t length);

/*!
 * @brief Returns the calculated hash value from an @ref XXH64_state_t.
 *
 * @param statePtr The state struct to calculate the hash from.
 *
 * @pre
 *  @p statePtr must not be `NULL`.
 *
 * @return The calculated 64-bit xxHash64 value from that state.
 *
 * @note
 *   Calling XXH64_digest() will not affect @p statePtr, so you can update,
 *   digest, and update again.
 *
 * @see @ref streaming_example "Streaming Example"
 */
XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH64_digest (XXH_NOESCAPE const XXH64_state_t* statePtr);
#endif /* !XXH_NO_STREAM */
/*******   Canonical representation   *******/

/*!
 * @brief Canonical (big endian) representation of @ref XXH64_hash_t.
 */
typedef struct { unsigned char digest[sizeof(XXH64_hash_t)]; } XXH64_canonical_t;

/*!
 * @brief Converts an @ref XXH64_hash_t to a big endian @ref XXH64_canonical_t.
 *
 * @param dst The @ref XXH64_canonical_t pointer to be stored to.
 * @param hash The @ref XXH64_hash_t to be converted.
 *
 * @pre
 *   @p dst must not be `NULL`.
 *
 * @see @ref canonical_representation_example "Canonical Representation Example"
 */
XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH_NOESCAPE XXH64_canonical_t* dst, XXH64_hash_t hash);

/*!
 * @brief Converts an @ref XXH64_canonical_t to a native @ref XXH64_hash_t.
 *
 * @param src The @ref XXH64_canonical_t to convert.
 *
 * @pre
 *   @p src must not be `NULL`.
 *
 * @return The converted hash.
 *
 * @see @ref canonical_representation_example "Canonical Representation Example"
 */
XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH64_hashFromCanonical(XXH_NOESCAPE const XXH64_canonical_t* src);

#ifndef XXH_NO_XXH3

/*!
 * @}
 * ************************************************************************
 * @defgroup XXH3_family XXH3 family
 * @ingroup public
 * @{
 *
 * XXH3 is a more recent hash algorithm featuring:
 *  - Improved speed for both small and large inputs
 *  - True 64-bit and 128-bit outputs
 *  - SIMD acceleration
 *  - Improved 32-bit viability
 *
 * Speed analysis methodology is explained here:
 *
 *    https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html
 *
 * Compared to XXH64, expect XXH3 to run approximately
 * ~2x faster on large inputs and >3x faster on small ones,
 * exact differences vary depending on platform.
 *
 * XXH3's speed benefits greatly from SIMD and 64-bit arithmetic,
 * but does not require it.
 * Most 32-bit and 64-bit targets that can run XXH32 smoothly can run XXH3
 * at competitive speeds, even without vector support. Further details are
 * explained in the implementation.
 *
 * XXH3 has a fast scalar implementation, but it also includes accelerated SIMD
 * implementations for many common platforms:
 *   - AVX512
 *   - AVX2
 *   - SSE2
 *   - ARM NEON
 *   - WebAssembly SIMD128
 *   - POWER8 VSX
 *   - s390x ZVector
 * This can be controlled via the @ref XXH_VECTOR macro, but it automatically
 * selects the best version according to predefined macros. For the x86 family, an
 * automatic runtime dispatcher is included separately in @ref xxh_x86dispatch.c.
 *
 * XXH3 implementation is portable:
 * it has a generic C90 formulation that can be compiled on any platform,
 * all implementations generate exactly the same hash value on all platforms.
 * Starting from v0.8.0, it's also labelled "stable", meaning that
 * any future version will also generate the same hash value.
 *
 * XXH3 offers 2 variants, _64bits and _128bits.
 *
 * When only 64 bits are needed, prefer invoking the _64bits variant, as it
 * reduces the amount of mixing, resulting in faster speed on small inputs.
 * It's also generally simpler to manipulate a scalar return type than a struct.
 *
 * The API supports one-shot hashing, streaming mode, and custom secrets.
 */
/*-**********************************************************************
*  XXH3 64-bit variant
************************************************************************/

/*!
 * @brief Calculates 64-bit unseeded variant of XXH3 hash of @p input.
 *
 * @param input  The block of data to be hashed, at least @p length bytes in size.
 * @param length The length of @p input, in bytes.
 *
 * @pre
 *   The memory between @p input and @p input + @p length must be valid,
 *   readable, contiguous memory. However, if @p length is `0`, @p input may be
 *   `NULL`. In C++, this also must be *TriviallyCopyable*.
 *
 * @return The calculated 64-bit XXH3 hash value.
 *
 * @note
 *   This is equivalent to @ref XXH3_64bits_withSeed() with a seed of `0`, however
 *   it may have slightly better performance due to constant propagation of the
 *   defaults.
 *
 * @see
 *    XXH3_64bits_withSeed(), XXH3_64bits_withSecret(): other seeding variants
 * @see @ref single_shot_example "Single Shot Example" for an example.
 */
XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits(XXH_NOESCAPE const void* input, size_t length);

/*!
 * @brief Calculates 64-bit seeded variant of XXH3 hash of @p input.
 *
 * @param input  The block of data to be hashed, at least @p length bytes in size.
 * @param length The length of @p input, in bytes.
 * @param seed   The 64-bit seed to alter the hash result predictably.
 *
 * @pre
 *   The memory between @p input and @p input + @p length must be valid,
 *   readable, contiguous memory. However, if @p length is `0`, @p input may be
 *   `NULL`. In C++, this also must be *TriviallyCopyable*.
 *
 * @return The calculated 64-bit XXH3 hash value.
 *
 * @note
 *    seed == 0 produces the same results as @ref XXH3_64bits().
 *
 * This variant generates a custom secret on the fly based on default secret
 * altered using the @p seed value.
 *
 * While this operation is decently fast, note that it's not completely free.
 *
 * @see @ref single_shot_example "Single Shot Example" for an example.
 */
XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits_withSeed(XXH_NOESCAPE const void* input, size_t length, XXH64_hash_t seed);

/*!
 * The bare minimum size for a custom secret.
 *
 * @see
 *  XXH3_64bits_withSecret(), XXH3_64bits_reset_withSecret(),
 *  XXH3_128bits_withSecret(), XXH3_128bits_reset_withSecret().
 */
#define XXH3_SECRET_SIZE_MIN 136

/*!
 * @brief Calculates 64-bit variant of XXH3 with a custom "secret".
 *
 * @param data       The block of data to be hashed, at least @p len bytes in size.
 * @param len        The length of @p data, in bytes.
 * @param secret     The secret data.
 * @param secretSize The length of @p secret, in bytes.
 *
 * @return The calculated 64-bit XXH3 hash value.
 *
 * @pre
 *   The memory between @p data and @p data + @p len must be valid,
 *   readable, contiguous memory. However, if @p length is `0`, @p data may be
 *   `NULL`. In C++, this also must be *TriviallyCopyable*.
 *
 * It's possible to provide any blob of bytes as a "secret" to generate the hash.
 * This makes it more difficult for an external actor to prepare an intentional collision.
 * The main condition is that @p secretSize *must* be large enough (>= @ref XXH3_SECRET_SIZE_MIN).
 * However, the quality of the secret impacts the dispersion of the hash algorithm.
 * Therefore, the secret _must_ look like a bunch of random bytes.
 * Avoid "trivial" or structured data such as repeated sequences or a text document.
 * Whenever in doubt about the "randomness" of the blob of bytes,
 * consider employing @ref XXH3_generateSecret() instead (see below).
 * It will generate a proper high entropy secret derived from the blob of bytes.
 * Another advantage of using XXH3_generateSecret() is that
 * it guarantees that all bits within the initial blob of bytes
 * will impact every bit of the output.
 * This is not necessarily the case when using the blob of bytes directly
 * because, when hashing _small_ inputs, only a portion of the secret is employed.
 *
 * @see @ref single_shot_example "Single Shot Example" for an example.
 */
XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits_withSecret(XXH_NOESCAPE const void* data, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize);


/*******   Streaming   *******/
#ifndef XXH_NO_STREAM
/*
 * Streaming requires state maintenance.
 * This operation costs memory and CPU.
 * As a consequence, streaming is slower than one-shot hashing.
 * For better performance, prefer one-shot functions whenever applicable.
 */

/*!
 * @brief The opaque state struct for the XXH3 streaming API.
 *
 * @see XXH3_state_s for details.
 * @see @ref streaming_example "Streaming Example"
 */
typedef struct XXH3_state_s XXH3_state_t;
XXH_PUBLIC_API XXH_MALLOCF XXH3_state_t* XXH3_createState(void);
XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr);

/*!
 * @brief Copies one @ref XXH3_state_t to another.
 *
 * @param dst_state The state to copy to.
 * @param src_state The state to copy from.
 * @pre
 *   @p dst_state and @p src_state must not be `NULL` and must not overlap.
 */
XXH_PUBLIC_API void XXH3_copyState(XXH_NOESCAPE XXH3_state_t* dst_state, XXH_NOESCAPE const XXH3_state_t* src_state);

/*!
 * @brief Resets an @ref XXH3_state_t to begin a new hash.
 *
 * @param statePtr The state struct to reset.
 *
 * @pre
 *   @p statePtr must not be `NULL`.
 *
 * @return @ref XXH_OK on success.
 * @return @ref XXH_ERROR on failure.
 *
 * @note
 *   - This function resets `statePtr` and generate a secret with default parameters.
 *   - Call this function before @ref XXH3_64bits_update().
 *   - Digest will be equivalent to `XXH3_64bits()`.
 *
 * @see @ref streaming_example "Streaming Example"
 *
 */
XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr);

/*!
 * @brief Resets an @ref XXH3_state_t with 64-bit seed to begin a new hash.
 *
 * @param statePtr The state struct to reset.
 * @param seed     The 64-bit seed to alter the hash result predictably.
 *
 * @pre
 *   @p statePtr must not be `NULL`.
 *
 * @return @ref XXH_OK on success.
 * @return @ref XXH_ERROR on failure.
 *
 * @note
 *   - This function resets `statePtr` and generate a secret from `seed`.
 *   - Call this function before @ref XXH3_64bits_update().
 *   - Digest will be equivalent to `XXH3_64bits_withSeed()`.
 *
 * @see @ref streaming_example "Streaming Example"
 *
 */
XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed);

/*!
 * @brief Resets an @ref XXH3_state_t with secret data to begin a new hash.
 *
 * @param statePtr The state struct to reset.
 * @param secret     The secret data.
 * @param secretSize The length of @p secret, in bytes.
 *
 * @pre
 *   @p statePtr must not be `NULL`.
 *
 * @return @ref XXH_OK on success.
 * @return @ref XXH_ERROR on failure.
 *
 * @note
 *   `secret` is referenced, it _must outlive_ the hash streaming session.
 *
 * Similar to one-shot API, `secretSize` must be >= @ref XXH3_SECRET_SIZE_MIN,
 * and the quality of produced hash values depends on secret's entropy
 * (secret's content should look like a bunch of random bytes).
 * When in doubt about the randomness of a candidate `secret`,
 * consider employing `XXH3_generateSecret()` instead (see below).
 *
 * @see @ref streaming_example "Streaming Example"
 */
XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize);

/*!
 * @brief Consumes a block of @p input to an @ref XXH3_state_t.
 *
 * @param statePtr The state struct to update.
 * @param input The block of data to be hashed, at least @p length bytes in size.
 * @param length The length of @p input, in bytes.
 *
 * @pre
 *   @p statePtr must not be `NULL`.
 * @pre
 *   The memory between @p input and @p input + @p length must be valid,
 *   readable, contiguous memory. However, if @p length is `0`, @p input may be
 *   `NULL`. In C++, this also must be *TriviallyCopyable*.
 *
 * @return @ref XXH_OK on success.
 * @return @ref XXH_ERROR on failure.
 *
 * @note Call this to incrementally consume blocks of data.
 *
 * @see @ref streaming_example "Streaming Example"
 */
XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update (XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* input, size_t length);

/*!
 * @brief Returns the calculated XXH3 64-bit hash value from an @ref XXH3_state_t.
 *
 * @param statePtr The state struct to calculate the hash from.
 *
 * @pre
 *  @p statePtr must not be `NULL`.
 *
 * @return The calculated XXH3 64-bit hash value from that state.
 *
 * @note
 *   Calling XXH3_64bits_digest() will not affect @p statePtr, so you can update,
 *   digest, and update again.
 *
 * @see @ref streaming_example "Streaming Example"
 */
XXH_PUBLIC_API XXH_PUREF XXH64_hash_t  XXH3_64bits_digest (XXH_NOESCAPE const XXH3_state_t* statePtr);
#endif /* !XXH_NO_STREAM */

/* note : canonical representation of XXH3 is the same as XXH64
 * since they both produce XXH64_hash_t values */


/*-**********************************************************************
*  XXH3 128-bit variant
************************************************************************/

/*!
 * @brief The return value from 128-bit hashes.
 *
 * Stored in little endian order, although the fields themselves are in native
 * endianness.
 */
typedef struct {
    XXH64_hash_t low64;   /*!< `value & 0xFFFFFFFFFFFFFFFF` */
    XXH64_hash_t high64;  /*!< `value >> 64` */
} XXH128_hash_t;

/*!
 * @brief Calculates 128-bit unseeded variant of XXH3 of @p data.
 *
 * @param data The block of data to be hashed, at least @p length bytes in size.
 * @param len  The length of @p data, in bytes.
 *
 * @return The calculated 128-bit variant of XXH3 value.
 *
 * The 128-bit variant of XXH3 has more strength, but it has a bit of overhead
 * for shorter inputs.
 *
 * This is equivalent to @ref XXH3_128bits_withSeed() with a seed of `0`, however
 * it may have slightly better performance due to constant propagation of the
 * defaults.
 *
 * @see XXH3_128bits_withSeed(), XXH3_128bits_withSecret(): other seeding variants
 * @see @ref single_shot_example "Single Shot Example" for an example.
 */
XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits(XXH_NOESCAPE const void* data, size_t len);
/*! @brief Calculates 128-bit seeded variant of XXH3 hash of @p data.
 *
 * @param data The block of data to be hashed, at least @p length bytes in size.
 * @param len  The length of @p data, in bytes.
 * @param seed The 64-bit seed to alter the hash result predictably.
 *
 * @return The calculated 128-bit variant of XXH3 value.
 *
 * @note
 *    seed == 0 produces the same results as @ref XXH3_64bits().
 *
 * This variant generates a custom secret on the fly based on default secret
 * altered using the @p seed value.
 *
 * While this operation is decently fast, note that it's not completely free.
 *
 * @see XXH3_128bits(), XXH3_128bits_withSecret(): other seeding variants
 * @see @ref single_shot_example "Single Shot Example" for an example.
 */
XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_withSeed(XXH_NOESCAPE const void* data, size_t len, XXH64_hash_t seed);
/*!
 * @brief Calculates 128-bit variant of XXH3 with a custom "secret".
 *
 * @param data       The block of data to be hashed, at least @p len bytes in size.
 * @param len        The length of @p data, in bytes.
 * @param secret     The secret data.
 * @param secretSize The length of @p secret, in bytes.
 *
 * @return The calculated 128-bit variant of XXH3 value.
 *
 * It's possible to provide any blob of bytes as a "secret" to generate the hash.
 * This makes it more difficult for an external actor to prepare an intentional collision.
 * The main condition is that @p secretSize *must* be large enough (>= @ref XXH3_SECRET_SIZE_MIN).
 * However, the quality of the secret impacts the dispersion of the hash algorithm.
 * Therefore, the secret _must_ look like a bunch of random bytes.
 * Avoid "trivial" or structured data such as repeated sequences or a text document.
 * Whenever in doubt about the "randomness" of the blob of bytes,
 * consider employing @ref XXH3_generateSecret() instead (see below).
 * It will generate a proper high entropy secret derived from the blob of bytes.
 * Another advantage of using XXH3_generateSecret() is that
 * it guarantees that all bits within the initial blob of bytes
 * will impact every bit of the output.
 * This is not necessarily the case when using the blob of bytes directly
 * because, when hashing _small_ inputs, only a portion of the secret is employed.
 *
 * @see @ref single_shot_example "Single Shot Example" for an example.
 */
XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_withSecret(XXH_NOESCAPE const void* data, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize);

/*******   Streaming   *******/
#ifndef XXH_NO_STREAM
/*
 * Streaming requires state maintenance.
 * This operation costs memory and CPU.
 * As a consequence, streaming is slower than one-shot hashing.
 * For better performance, prefer one-shot functions whenever applicable.
 *
 * XXH3_128bits uses the same XXH3_state_t as XXH3_64bits().
 * Use already declared XXH3_createState() and XXH3_freeState().
 *
 * All reset and streaming functions have same meaning as their 64-bit counterpart.
 */

/*!
 * @brief Resets an @ref XXH3_state_t to begin a new hash.
 *
 * @param statePtr The state struct to reset.
 *
 * @pre
 *   @p statePtr must not be `NULL`.
 *
 * @return @ref XXH_OK on success.
 * @return @ref XXH_ERROR on failure.
 *
 * @note
 *   - This function resets `statePtr` and generate a secret with default parameters.
 *   - Call it before @ref XXH3_128bits_update().
 *   - Digest will be equivalent to `XXH3_128bits()`.
 *
 * @see @ref streaming_example "Streaming Example"
 */
XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr);

/*!
 * @brief Resets an @ref XXH3_state_t with 64-bit seed to begin a new hash.
 *
 * @param statePtr The state struct to reset.
 * @param seed     The 64-bit seed to alter the hash result predictably.
 *
 * @pre
 *   @p statePtr must not be `NULL`.
 *
 * @return @ref XXH_OK on success.
 * @return @ref XXH_ERROR on failure.
 *
 * @note
 *   - This function resets `statePtr` and generate a secret from `seed`.
 *   - Call it before @ref XXH3_128bits_update().
 *   - Digest will be equivalent to `XXH3_128bits_withSeed()`.
 *
 * @see @ref streaming_example "Streaming Example"
 */
XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed);
/*!
 * @brief Resets an @ref XXH3_state_t with secret data to begin a new hash.
 *
 * @param statePtr   The state struct to reset.
 * @param secret     The secret data.
 * @param secretSize The length of @p secret, in bytes.
 *
 * @pre
 *   @p statePtr must not be `NULL`.
 *
 * @return @ref XXH_OK on success.
 * @return @ref XXH_ERROR on failure.
 *
 * `secret` is referenced, it _must outlive_ the hash streaming session.
 * Similar to one-shot API, `secretSize` must be >= @ref XXH3_SECRET_SIZE_MIN,
 * and the quality of produced hash values depends on secret's entropy
 * (secret's content should look like a bunch of random bytes).
 * When in doubt about the randomness of a candidate `secret`,
 * consider employing `XXH3_generateSecret()` instead (see below).
 *
 * @see @ref streaming_example "Streaming Example"
 */
XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize);

/*!
 * @brief Consumes a block of @p input to an @ref XXH3_state_t.
 *
 * Call this to incrementally consume blocks of data.
 *
 * @param statePtr The state struct to update.
 * @param input The block of data to be hashed, at least @p length bytes in size.
 * @param length The length of @p input, in bytes.
 *
 * @pre
 *   @p statePtr must not be `NULL`.
 *
 * @return @ref XXH_OK on success.
 * @return @ref XXH_ERROR on failure.
 *
 * @note
 *   The memory between @p input and @p input + @p length must be valid,
 *   readable, contiguous memory. However, if @p length is `0`, @p input may be
 *   `NULL`. In C++, this also must be *TriviallyCopyable*.
 *
 */
XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update (XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* input, size_t length);

/*!
 * @brief Returns the calculated XXH3 128-bit hash value from an @ref XXH3_state_t.
 *
 * @param statePtr The state struct to calculate the hash from.
 *
 * @pre
 *  @p statePtr must not be `NULL`.
 *
 * @return The calculated XXH3 128-bit hash value from that state.
 *
 * @note
 *   Calling XXH3_128bits_digest() will not affect @p statePtr, so you can update,
 *   digest, and update again.
 *
 */
XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_digest (XXH_NOESCAPE const XXH3_state_t* statePtr);
#endif /* !XXH_NO_STREAM */

/* Following helper functions make it possible to compare XXH128_hast_t values.
 * Since XXH128_hash_t is a structure, this capability is not offered by the language.
 * Note: For better performance, these functions can be inlined using XXH_INLINE_ALL */

/*!
 * @brief Check equality of two XXH128_hash_t values
 *
 * @param h1 The 128-bit hash value.
 * @param h2 Another 128-bit hash value.
 *
 * @return `1` if `h1` and `h2` are equal.
 * @return `0` if they are not.
 */
XXH_PUBLIC_API XXH_PUREF int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2);

/*!
 * @brief Compares two @ref XXH128_hash_t
 *
 * This comparator is compatible with stdlib's `qsort()`/`bsearch()`.
 *
 * @param h128_1 Left-hand side value
 * @param h128_2 Right-hand side value
 *
 * @return >0 if @p h128_1  > @p h128_2
 * @return =0 if @p h128_1 == @p h128_2
 * @return <0 if @p h128_1  < @p h128_2
 */
XXH_PUBLIC_API XXH_PUREF int XXH128_cmp(XXH_NOESCAPE const void* h128_1, XXH_NOESCAPE const void* h128_2);


/*******   Canonical representation   *******/
typedef struct { unsigned char digest[sizeof(XXH128_hash_t)]; } XXH128_canonical_t;


/*!
 * @brief Converts an @ref XXH128_hash_t to a big endian @ref XXH128_canonical_t.
 *
 * @param dst  The @ref XXH128_canonical_t pointer to be stored to.
 * @param hash The @ref XXH128_hash_t to be converted.
 *
 * @pre
 *   @p dst must not be `NULL`.
 * @see @ref canonical_representation_example "Canonical Representation Example"
 */
XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH_NOESCAPE XXH128_canonical_t* dst, XXH128_hash_t hash);

/*!
 * @brief Converts an @ref XXH128_canonical_t to a native @ref XXH128_hash_t.
 *
 * @param src The @ref XXH128_canonical_t to convert.
 *
 * @pre
 *   @p src must not be `NULL`.
 *
 * @return The converted hash.
 * @see @ref canonical_representation_example "Canonical Representation Example"
 */
XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH128_hashFromCanonical(XXH_NOESCAPE const XXH128_canonical_t* src);


#endif  /* !XXH_NO_XXH3 */
#endif  /* XXH_NO_LONG_LONG */

/*!
 * @}
 */
#endif /* XXHASH_H_5627135585666179 */



#if defined(XXH_STATIC_LINKING_ONLY) && !defined(XXHASH_H_STATIC_13879238742)
#define XXHASH_H_STATIC_13879238742
/* ****************************************************************************
 * This section contains declarations which are not guaranteed to remain stable.
 * They may change in future versions, becoming incompatible with a different
 * version of the library.
 * These declarations should only be used with static linking.
 * Never use them in association with dynamic linking!
 ***************************************************************************** */

/*
 * These definitions are only present to allow static allocation
 * of XXH states, on stack or in a struct, for example.
 * Never **ever** access their members directly.
 */

/*!
 * @internal
 * @brief Structure for XXH32 streaming API.
 *
 * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY,
 * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is
 * an opaque type. This allows fields to safely be changed.
 *
 * Typedef'd to @ref XXH32_state_t.
 * Do not access the members of this struct directly.
 * @see XXH64_state_s, XXH3_state_s
 */
struct XXH32_state_s {
   XXH32_hash_t total_len_32; /*!< Total length hashed, modulo 2^32 */
   XXH32_hash_t large_len;    /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */
   XXH32_hash_t v[4];         /*!< Accumulator lanes */
   XXH32_hash_t mem32[4];     /*!< Internal buffer for partial reads. Treated as unsigned char[16]. */
   XXH32_hash_t memsize;      /*!< Amount of data in @ref mem32 */
   XXH32_hash_t reserved;     /*!< Reserved field. Do not read nor write to it. */
};   /* typedef'd to XXH32_state_t */


#ifndef XXH_NO_LONG_LONG  /* defined when there is no 64-bit support */

/*!
 * @internal
 * @brief Structure for XXH64 streaming API.
 *
 * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY,
 * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is
 * an opaque type. This allows fields to safely be changed.
 *
 * Typedef'd to @ref XXH64_state_t.
 * Do not access the members of this struct directly.
 * @see XXH32_state_s, XXH3_state_s
 */
struct XXH64_state_s {
   XXH64_hash_t total_len;    /*!< Total length hashed. This is always 64-bit. */
   XXH64_hash_t v[4];         /*!< Accumulator lanes */
   XXH64_hash_t mem64[4];     /*!< Internal buffer for partial reads. Treated as unsigned char[32]. */
   XXH32_hash_t memsize;      /*!< Amount of data in @ref mem64 */
   XXH32_hash_t reserved32;   /*!< Reserved field, needed for padding anyways*/
   XXH64_hash_t reserved64;   /*!< Reserved field. Do not read or write to it. */
};   /* typedef'd to XXH64_state_t */

#ifndef XXH_NO_XXH3

#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* >= C11 */
#  include <stdalign.h>
#  define XXH_ALIGN(n)      alignas(n)
#elif defined(__cplusplus) && (__cplusplus >= 201103L) /* >= C++11 */
/* In C++ alignas() is a keyword */
#  define XXH_ALIGN(n)      alignas(n)
#elif defined(__GNUC__)
#  define XXH_ALIGN(n)      __attribute__ ((aligned(n)))
#elif defined(_MSC_VER)
#  define XXH_ALIGN(n)      __declspec(align(n))
#else
#  define XXH_ALIGN(n)   /* disabled */
#endif

/* Old GCC versions only accept the attribute after the type in structures. */
#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L))   /* C11+ */ \
    && ! (defined(__cplusplus) && (__cplusplus >= 201103L)) /* >= C++11 */ \
    && defined(__GNUC__)
#   define XXH_ALIGN_MEMBER(align, type) type XXH_ALIGN(align)
#else
#   define XXH_ALIGN_MEMBER(align, type) XXH_ALIGN(align) type
#endif

/*!
 * @brief The size of the internal XXH3 buffer.
 *
 * This is the optimal update size for incremental hashing.
 *
 * @see XXH3_64b_update(), XXH3_128b_update().
 */
#define XXH3_INTERNALBUFFER_SIZE 256

/*!
 * @internal
 * @brief Default size of the secret buffer (and @ref XXH3_kSecret).
 *
 * This is the size used in @ref XXH3_kSecret and the seeded functions.
 *
 * Not to be confused with @ref XXH3_SECRET_SIZE_MIN.
 */
#define XXH3_SECRET_DEFAULT_SIZE 192

/*!
 * @internal
 * @brief Structure for XXH3 streaming API.
 *
 * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY,
 * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined.
 * Otherwise it is an opaque type.
 * Never use this definition in combination with dynamic library.
 * This allows fields to safely be changed in the future.
 *
 * @note ** This structure has a strict alignment requirement of 64 bytes!! **
 * Do not allocate this with `malloc()` or `new`,
 * it will not be sufficiently aligned.
 * Use @ref XXH3_createState() and @ref XXH3_freeState(), or stack allocation.
 *
 * Typedef'd to @ref XXH3_state_t.
 * Do never access the members of this struct directly.
 *
 * @see XXH3_INITSTATE() for stack initialization.
 * @see XXH3_createState(), XXH3_freeState().
 * @see XXH32_state_s, XXH64_state_s
 */
struct XXH3_state_s {
   XXH_ALIGN_MEMBER(64, XXH64_hash_t acc[8]);
       /*!< The 8 accumulators. See @ref XXH32_state_s::v and @ref XXH64_state_s::v */
   XXH_ALIGN_MEMBER(64, unsigned char customSecret[XXH3_SECRET_DEFAULT_SIZE]);
       /*!< Used to store a custom secret generated from a seed. */
   XXH_ALIGN_MEMBER(64, unsigned char buffer[XXH3_INTERNALBUFFER_SIZE]);
       /*!< The internal buffer. @see XXH32_state_s::mem32 */
   XXH32_hash_t bufferedSize;
       /*!< The amount of memory in @ref buffer, @see XXH32_state_s::memsize */
   XXH32_hash_t useSeed;
       /*!< Reserved field. Needed for padding on 64-bit. */
   size_t nbStripesSoFar;
       /*!< Number or stripes processed. */
   XXH64_hash_t totalLen;
       /*!< Total length hashed. 64-bit even on 32-bit targets. */
   size_t nbStripesPerBlock;
       /*!< Number of stripes per block. */
   size_t secretLimit;
       /*!< Size of @ref customSecret or @ref extSecret */
   XXH64_hash_t seed;
       /*!< Seed for _withSeed variants. Must be zero otherwise, @see XXH3_INITSTATE() */
   XXH64_hash_t reserved64;
       /*!< Reserved field. */
   const unsigned char* extSecret;
       /*!< Reference to an external secret for the _withSecret variants, NULL
        *   for other variants. */
   /* note: there may be some padding at the end due to alignment on 64 bytes */
}; /* typedef'd to XXH3_state_t */

#undef XXH_ALIGN_MEMBER

/*!
 * @brief Initializes a stack-allocated `XXH3_state_s`.
 *
 * When the @ref XXH3_state_t structure is merely emplaced on stack,
 * it should be initialized with XXH3_INITSTATE() or a memset()
 * in case its first reset uses XXH3_NNbits_reset_withSeed().
 * This init can be omitted if the first reset uses default or _withSecret mode.
 * This operation isn't necessary when the state is created with XXH3_createState().
 * Note that this doesn't prepare the state for a streaming operation,
 * it's still necessary to use XXH3_NNbits_reset*() afterwards.
 */
#define XXH3_INITSTATE(XXH3_state_ptr)                       \
    do {                                                     \
        XXH3_state_t* tmp_xxh3_state_ptr = (XXH3_state_ptr); \
        tmp_xxh3_state_ptr->seed = 0;                        \
        tmp_xxh3_state_ptr->extSecret = NULL;                \
    } while(0)


/*!
 * @brief Calculates the 128-bit hash of @p data using XXH3.
 *
 * @param data The block of data to be hashed, at least @p len bytes in size.
 * @param len  The length of @p data, in bytes.
 * @param seed The 64-bit seed to alter the hash's output predictably.
 *
 * @pre
 *   The memory between @p data and @p data + @p len must be valid,
 *   readable, contiguous memory. However, if @p len is `0`, @p data may be
 *   `NULL`. In C++, this also must be *TriviallyCopyable*.
 *
 * @return The calculated 128-bit XXH3 value.
 *
 * @see @ref single_shot_example "Single Shot Example" for an example.
 */
XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH128(XXH_NOESCAPE const void* data, size_t len, XXH64_hash_t seed);


/* ===   Experimental API   === */
/* Symbols defined below must be considered tied to a specific library version. */

/*!
 * @brief Derive a high-entropy secret from any user-defined content, named customSeed.
 *
 * @param secretBuffer    A writable buffer for derived high-entropy secret data.
 * @param secretSize      Size of secretBuffer, in bytes.  Must be >= XXH3_SECRET_DEFAULT_SIZE.
 * @param customSeed      A user-defined content.
 * @param customSeedSize  Size of customSeed, in bytes.
 *
 * @return @ref XXH_OK on success.
 * @return @ref XXH_ERROR on failure.
 *
 * The generated secret can be used in combination with `*_withSecret()` functions.
 * The `_withSecret()` variants are useful to provide a higher level of protection
 * than 64-bit seed, as it becomes much more difficult for an external actor to
 * guess how to impact the calculation logic.
 *
 * The function accepts as input a custom seed of any length and any content,
 * and derives from it a high-entropy secret of length @p secretSize into an
 * already allocated buffer @p secretBuffer.
 *
 * The generated secret can then be used with any `*_withSecret()` variant.
 * The functions @ref XXH3_128bits_withSecret(), @ref XXH3_64bits_withSecret(),
 * @ref XXH3_128bits_reset_withSecret() and @ref XXH3_64bits_reset_withSecret()
 * are part of this list. They all accept a `secret` parameter
 * which must be large enough for implementation reasons (>= @ref XXH3_SECRET_SIZE_MIN)
 * _and_ feature very high entropy (consist of random-looking bytes).
 * These conditions can be a high bar to meet, so @ref XXH3_generateSecret() can
 * be employed to ensure proper quality.
 *
 * @p customSeed can be anything. It can have any size, even small ones,
 * and its content can be anything, even "poor entropy" sources such as a bunch
 * of zeroes. The resulting `secret` will nonetheless provide all required qualities.
 *
 * @pre
 *   - @p secretSize must be >= @ref XXH3_SECRET_SIZE_MIN
 *   - When @p customSeedSize > 0, supplying NULL as customSeed is undefined behavior.
 *
 * Example code:
 * @code{.c}
 *    #include <stdio.h>
 *    #include <stdlib.h>
 *    #include <string.h>
 *    #define XXH_STATIC_LINKING_ONLY // expose unstable API
 *    #include "xxhash.h"
 *    // Hashes argv[2] using the entropy from argv[1].
 *    int main(int argc, char* argv[])
 *    {
 *        char secret[XXH3_SECRET_SIZE_MIN];
 *        if (argv != 3) { return 1; }
 *        XXH3_generateSecret(secret, sizeof(secret), argv[1], strlen(argv[1]));
 *        XXH64_hash_t h = XXH3_64bits_withSecret(
 *             argv[2], strlen(argv[2]),
 *             secret, sizeof(secret)
 *        );
 *        printf("%016llx\n", (unsigned long long) h);
 *    }
 * @endcode
 */
XXH_PUBLIC_API XXH_errorcode XXH3_generateSecret(XXH_NOESCAPE void* secretBuffer, size_t secretSize, XXH_NOESCAPE const void* customSeed, size_t customSeedSize);

/*!
 * @brief Generate the same secret as the _withSeed() variants.
 *
 * @param secretBuffer A writable buffer of @ref XXH3_SECRET_SIZE_MIN bytes
 * @param seed         The 64-bit seed to alter the hash result predictably.
 *
 * The generated secret can be used in combination with
 *`*_withSecret()` and `_withSecretandSeed()` variants.
 *
 * Example C++ `std::string` hash class:
 * @code{.cpp}
 *    #include <string>
 *    #define XXH_STATIC_LINKING_ONLY // expose unstable API
 *    #include "xxhash.h"
 *    // Slow, seeds each time
 *    class HashSlow {
 *        XXH64_hash_t seed;
 *    public:
 *        HashSlow(XXH64_hash_t s) : seed{s} {}
 *        size_t operator()(const std::string& x) const {
 *            return size_t{XXH3_64bits_withSeed(x.c_str(), x.length(), seed)};
 *        }
 *    };
 *    // Fast, caches the seeded secret for future uses.
 *    class HashFast {
 *        unsigned char secret[XXH3_SECRET_SIZE_MIN];
 *    public:
 *        HashFast(XXH64_hash_t s) {
 *            XXH3_generateSecret_fromSeed(secret, seed);
 *        }
 *        size_t operator()(const std::string& x) const {
 *            return size_t{
 *                XXH3_64bits_withSecret(x.c_str(), x.length(), secret, sizeof(secret))
 *            };
 *        }
 *    };
 * @endcode
 */
XXH_PUBLIC_API void XXH3_generateSecret_fromSeed(XXH_NOESCAPE void* secretBuffer, XXH64_hash_t seed);

/*!
 * @brief Calculates 64/128-bit seeded variant of XXH3 hash of @p data.
 *
 * @param data       The block of data to be hashed, at least @p len bytes in size.
 * @param len        The length of @p data, in bytes.
 * @param secret     The secret data.
 * @param secretSize The length of @p secret, in bytes.
 * @param seed       The 64-bit seed to alter the hash result predictably.
 *
 * These variants generate hash values using either
 * @p seed for "short" keys (< @ref XXH3_MIDSIZE_MAX = 240 bytes)
 * or @p secret for "large" keys (>= @ref XXH3_MIDSIZE_MAX).
 *
 * This generally benefits speed, compared to `_withSeed()` or `_withSecret()`.
 * `_withSeed()` has to generate the secret on the fly for "large" keys.
 * It's fast, but can be perceptible for "not so large" keys (< 1 KB).
 * `_withSecret()` has to generate the masks on the fly for "small" keys,
 * which requires more instructions than _withSeed() variants.
 * Therefore, _withSecretandSeed variant combines the best of both worlds.
 *
 * When @p secret has been generated by XXH3_generateSecret_fromSeed(),
 * this variant produces *exactly* the same results as `_withSeed()` variant,
 * hence offering only a pure speed benefit on "large" input,
 * by skipping the need to regenerate the secret for every large input.
 *
 * Another usage scenario is to hash the secret to a 64-bit hash value,
 * for example with XXH3_64bits(), which then becomes the seed,
 * and then employ both the seed and the secret in _withSecretandSeed().
 * On top of speed, an added benefit is that each bit in the secret
 * has a 50% chance to swap each bit in the output, via its impact to the seed.
 *
 * This is not guaranteed when using the secret directly in "small data" scenarios,
 * because only portions of the secret are employed for small data.
 */
XXH_PUBLIC_API XXH_PUREF XXH64_hash_t
XXH3_64bits_withSecretandSeed(XXH_NOESCAPE const void* data, size_t len,
                              XXH_NOESCAPE const void* secret, size_t secretSize,
                              XXH64_hash_t seed);
/*!
 * @brief Calculates 128-bit seeded variant of XXH3 hash of @p data.
 *
 * @param input      The block of data to be hashed, at least @p len bytes in size.
 * @param length     The length of @p data, in bytes.
 * @param secret     The secret data.
 * @param secretSize The length of @p secret, in bytes.
 * @param seed64     The 64-bit seed to alter the hash result predictably.
 *
 * @return @ref XXH_OK on success.
 * @return @ref XXH_ERROR on failure.
 *
 * @see XXH3_64bits_withSecretandSeed()
 */
XXH_PUBLIC_API XXH_PUREF XXH128_hash_t
XXH3_128bits_withSecretandSeed(XXH_NOESCAPE const void* input, size_t length,
                               XXH_NOESCAPE const void* secret, size_t secretSize,
                               XXH64_hash_t seed64);
#ifndef XXH_NO_STREAM
/*!
 * @brief Resets an @ref XXH3_state_t with secret data to begin a new hash.
 *
 * @param statePtr   A pointer to an @ref XXH3_state_t allocated with @ref XXH3_createState().
 * @param secret     The secret data.
 * @param secretSize The length of @p secret, in bytes.
 * @param seed64     The 64-bit seed to alter the hash result predictably.
 *
 * @return @ref XXH_OK on success.
 * @return @ref XXH_ERROR on failure.
 *
 * @see XXH3_64bits_withSecretandSeed()
 */
XXH_PUBLIC_API XXH_errorcode
XXH3_64bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr,
                                    XXH_NOESCAPE const void* secret, size_t secretSize,
                                    XXH64_hash_t seed64);
/*!
 * @brief Resets an @ref XXH3_state_t with secret data to begin a new hash.
 *
 * @param statePtr   A pointer to an @ref XXH3_state_t allocated with @ref XXH3_createState().
 * @param secret     The secret data.
 * @param secretSize The length of @p secret, in bytes.
 * @param seed64     The 64-bit seed to alter the hash result predictably.
 *
 * @return @ref XXH_OK on success.
 * @return @ref XXH_ERROR on failure.
 *
 * @see XXH3_64bits_withSecretandSeed()
 */
XXH_PUBLIC_API XXH_errorcode
XXH3_128bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr,
                                     XXH_NOESCAPE const void* secret, size_t secretSize,
                                     XXH64_hash_t seed64);
#endif /* !XXH_NO_STREAM */

#endif  /* !XXH_NO_XXH3 */
#endif  /* XXH_NO_LONG_LONG */
#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)
#  define XXH_IMPLEMENTATION
#endif

#endif  /* defined(XXH_STATIC_LINKING_ONLY) && !defined(XXHASH_H_STATIC_13879238742) */


/* ======================================================================== */
/* ======================================================================== */
/* ======================================================================== */


/*-**********************************************************************
 * xxHash implementation
 *-**********************************************************************
 * xxHash's implementation used to be hosted inside xxhash.c.
 *
 * However, inlining requires implementation to be visible to the compiler,
 * hence be included alongside the header.
 * Previously, implementation was hosted inside xxhash.c,
 * which was then #included when inlining was activated.
 * This construction created issues with a few build and install systems,
 * as it required xxhash.c to be stored in /include directory.
 *
 * xxHash implementation is now directly integrated within xxhash.h.
 * As a consequence, xxhash.c is no longer needed in /include.
 *
 * xxhash.c is still available and is still useful.
 * In a "normal" setup, when xxhash is not inlined,
 * xxhash.h only exposes the prototypes and public symbols,
 * while xxhash.c can be built into an object file xxhash.o
 * which can then be linked into the final binary.
 ************************************************************************/

#if ( defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) \
   || defined(XXH_IMPLEMENTATION) ) && !defined(XXH_IMPLEM_13a8737387)
#  define XXH_IMPLEM_13a8737387

/* *************************************
*  Tuning parameters
***************************************/

/*!
 * @defgroup tuning Tuning parameters
 * @{
 *
 * Various macros to control xxHash's behavior.
 */
#ifdef XXH_DOXYGEN
/*!
 * @brief Define this to disable 64-bit code.
 *
 * Useful if only using the @ref XXH32_family and you have a strict C90 compiler.
 */
#  define XXH_NO_LONG_LONG
#  undef XXH_NO_LONG_LONG /* don't actually */
/*!
 * @brief Controls how unaligned memory is accessed.
 *
 * By default, access to unaligned memory is controlled by `memcpy()`, which is
 * safe and portable.
 *
 * Unfortunately, on some target/compiler combinations, the generated assembly
 * is sub-optimal.
 *
 * The below switch allow selection of a different access method
 * in the search for improved performance.
 *
 * @par Possible options:
 *
 *  - `XXH_FORCE_MEMORY_ACCESS=0` (default): `memcpy`
 *   @par
 *     Use `memcpy()`. Safe and portable. Note that most modern compilers will
 *     eliminate the function call and treat it as an unaligned access.
 *
 *  - `XXH_FORCE_MEMORY_ACCESS=1`: `__attribute__((aligned(1)))`
 *   @par
 *     Depends on compiler extensions and is therefore not portable.
 *     This method is safe _if_ your compiler supports it,
 *     and *generally* as fast or faster than `memcpy`.
 *
 *  - `XXH_FORCE_MEMORY_ACCESS=2`: Direct cast
 *  @par
 *     Casts directly and dereferences. This method doesn't depend on the
 *     compiler, but it violates the C standard as it directly dereferences an
 *     unaligned pointer. It can generate buggy code on targets which do not
 *     support unaligned memory accesses, but in some circumstances, it's the
 *     only known way to get the most performance.
 *
 *  - `XXH_FORCE_MEMORY_ACCESS=3`: Byteshift
 *  @par
 *     Also portable. This can generate the best code on old compilers which don't
 *     inline small `memcpy()` calls, and it might also be faster on big-endian
 *     systems which lack a native byteswap instruction. However, some compilers
 *     will emit literal byteshifts even if the target supports unaligned access.
 *
 *
 * @warning
 *   Methods 1 and 2 rely on implementation-defined behavior. Use these with
 *   care, as what works on one compiler/platform/optimization level may cause
 *   another to read garbage data or even crash.
 *
 * See https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html for details.
 *
 * Prefer these methods in priority order (0 > 3 > 1 > 2)
 */
#  define XXH_FORCE_MEMORY_ACCESS 0

/*!
 * @def XXH_SIZE_OPT
 * @brief Controls how much xxHash optimizes for size.
 *
 * xxHash, when compiled, tends to result in a rather large binary size. This
 * is mostly due to heavy usage to forced inlining and constant folding of the
 * @ref XXH3_family to increase performance.
 *
 * However, some developers prefer size over speed. This option can
 * significantly reduce the size of the generated code. When using the `-Os`
 * or `-Oz` options on GCC or Clang, this is defined to 1 by default,
 * otherwise it is defined to 0.
 *
 * Most of these size optimizations can be controlled manually.
 *
 * This is a number from 0-2.
 *  - `XXH_SIZE_OPT` == 0: Default. xxHash makes no size optimizations. Speed
 *    comes first.
 *  - `XXH_SIZE_OPT` == 1: Default for `-Os` and `-Oz`. xxHash is more
 *    conservative and disables hacks that increase code size. It implies the
 *    options @ref XXH_NO_INLINE_HINTS == 1, @ref XXH_FORCE_ALIGN_CHECK == 0,
 *    and @ref XXH3_NEON_LANES == 8 if they are not already defined.
 *  - `XXH_SIZE_OPT` == 2: xxHash tries to make itself as small as possible.
 *    Performance may cry. For example, the single shot functions just use the
 *    streaming API.
 */
#  define XXH_SIZE_OPT 0

/*!
 * @def XXH_FORCE_ALIGN_CHECK
 * @brief If defined to non-zero, adds a special path for aligned inputs (XXH32()
 * and XXH64() only).
 *
 * This is an important performance trick for architectures without decent
 * unaligned memory access performance.
 *
 * It checks for input alignment, and when conditions are met, uses a "fast
 * path" employing direct 32-bit/64-bit reads, resulting in _dramatically
 * faster_ read speed.
 *
 * The check costs one initial branch per hash, which is generally negligible,
 * but not zero.
 *
 * Moreover, it's not useful to generate an additional code path if memory
 * access uses the same instruction for both aligned and unaligned
 * addresses (e.g. x86 and aarch64).
 *
 * In these cases, the alignment check can be removed by setting this macro to 0.
 * Then the code will always use unaligned memory access.
 * Align check is automatically disabled on x86, x64, ARM64, and some ARM chips
 * which are platforms known to offer good unaligned memory accesses performance.
 *
 * It is also disabled by default when @ref XXH_SIZE_OPT >= 1.
 *
 * This option does not affect XXH3 (only XXH32 and XXH64).
 */
#  define XXH_FORCE_ALIGN_CHECK 0

/*!
 * @def XXH_NO_INLINE_HINTS
 * @brief When non-zero, sets all functions to `static`.
 *
 * By default, xxHash tries to force the compiler to inline almost all internal
 * functions.
 *
 * This can usually improve performance due to reduced jumping and improved
 * constant folding, but significantly increases the size of the binary which
 * might not be favorable.
 *
 * Additionally, sometimes the forced inlining can be detrimental to performance,
 * depending on the architecture.
 *
 * XXH_NO_INLINE_HINTS marks all internal functions as static, giving the
 * compiler full control on whether to inline or not.
 *
 * When not optimizing (-O0), using `-fno-inline` with GCC or Clang, or if
 * @ref XXH_SIZE_OPT >= 1, this will automatically be defined.
 */
#  define XXH_NO_INLINE_HINTS 0

/*!
 * @def XXH3_INLINE_SECRET
 * @brief Determines whether to inline the XXH3 withSecret code.
 *
 * When the secret size is known, the compiler can improve the performance
 * of XXH3_64bits_withSecret() and XXH3_128bits_withSecret().
 *
 * However, if the secret size is not known, it doesn't have any benefit. This
 * happens when xxHash is compiled into a global symbol. Therefore, if
 * @ref XXH_INLINE_ALL is *not* defined, this will be defined to 0.
 *
 * Additionally, this defaults to 0 on GCC 12+, which has an issue with function pointers
 * that are *sometimes* force inline on -Og, and it is impossible to automatically
 * detect this optimization level.
 */
#  define XXH3_INLINE_SECRET 0

/*!
 * @def XXH32_ENDJMP
 * @brief Whether to use a jump for `XXH32_finalize`.
 *
 * For performance, `XXH32_finalize` uses multiple branches in the finalizer.
 * This is generally preferable for performance,
 * but depending on exact architecture, a jmp may be preferable.
 *
 * This setting is only possibly making a difference for very small inputs.
 */
#  define XXH32_ENDJMP 0

/*!
 * @internal
 * @brief Redefines old internal names.
 *
 * For compatibility with code that uses xxHash's internals before the names
 * were changed to improve namespacing. There is no other reason to use this.
 */
#  define XXH_OLD_NAMES
#  undef XXH_OLD_NAMES /* don't actually use, it is ugly. */

/*!
 * @def XXH_NO_STREAM
 * @brief Disables the streaming API.
 *
 * When xxHash is not inlined and the streaming functions are not used, disabling
 * the streaming functions can improve code size significantly, especially with
 * the @ref XXH3_family which tends to make constant folded copies of itself.
 */
#  define XXH_NO_STREAM
#  undef XXH_NO_STREAM /* don't actually */
#endif /* XXH_DOXYGEN */
/*!
 * @}
 */

#ifndef XXH_FORCE_MEMORY_ACCESS   /* can be defined externally, on command line for example */
   /* prefer __packed__ structures (method 1) for GCC
    * < ARMv7 with unaligned access (e.g. Raspbian armhf) still uses byte shifting, so we use memcpy
    * which for some reason does unaligned loads. */
#  if defined(__GNUC__) && !(defined(__ARM_ARCH) && __ARM_ARCH < 7 && defined(__ARM_FEATURE_UNALIGNED))
#    define XXH_FORCE_MEMORY_ACCESS 1
#  endif
#endif

#ifndef XXH_SIZE_OPT
   /* default to 1 for -Os or -Oz */
#  if (defined(__GNUC__) || defined(__clang__)) && defined(__OPTIMIZE_SIZE__)
#    define XXH_SIZE_OPT 1
#  else
#    define XXH_SIZE_OPT 0
#  endif
#endif

#ifndef XXH_FORCE_ALIGN_CHECK  /* can be defined externally */
   /* don't check on sizeopt, x86, aarch64, or arm when unaligned access is available */
#  if XXH_SIZE_OPT >= 1 || \
      defined(__i386)  || defined(__x86_64__) || defined(__aarch64__) || defined(__ARM_FEATURE_UNALIGNED) \
   || defined(_M_IX86) || defined(_M_X64)     || defined(_M_ARM64)    || defined(_M_ARM) /* visual */
#    define XXH_FORCE_ALIGN_CHECK 0
#  else
#    define XXH_FORCE_ALIGN_CHECK 1
#  endif
#endif

#ifndef XXH_NO_INLINE_HINTS
#  if XXH_SIZE_OPT >= 1 || defined(__NO_INLINE__)  /* -O0, -fno-inline */
#    define XXH_NO_INLINE_HINTS 1
#  else
#    define XXH_NO_INLINE_HINTS 0
#  endif
#endif

#ifndef XXH3_INLINE_SECRET
#  if (defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 12) \
     || !defined(XXH_INLINE_ALL)
#    define XXH3_INLINE_SECRET 0
#  else
#    define XXH3_INLINE_SECRET 1
#  endif
#endif

#ifndef XXH32_ENDJMP
/* generally preferable for performance */
#  define XXH32_ENDJMP 0
#endif

/*!
 * @defgroup impl Implementation
 * @{
 */


/* *************************************
*  Includes & Memory related functions
***************************************/
#if defined(XXH_NO_STREAM)
/* nothing */
#elif defined(XXH_NO_STDLIB)

/* When requesting to disable any mention of stdlib,
 * the library loses the ability to invoked malloc / free.
 * In practice, it means that functions like `XXH*_createState()`
 * will always fail, and return NULL.
 * This flag is useful in situations where
 * xxhash.h is integrated into some kernel, embedded or limited environment
 * without access to dynamic allocation.
 */

static XXH_CONSTF void* XXH_malloc(size_t s) { (void)s; return NULL; }
static void XXH_free(void* p) { (void)p; }

#else

/*
 * Modify the local functions below should you wish to use
 * different memory routines for malloc() and free()
 */
#include <stdlib.h>

/*!
 * @internal
 * @brief Modify this function to use a different routine than malloc().
 */
static XXH_MALLOCF void* XXH_malloc(size_t s) { return malloc(s); }

/*!
 * @internal
 * @brief Modify this function to use a different routine than free().
 */
static void XXH_free(void* p) { free(p); }

#endif  /* XXH_NO_STDLIB */

#include <string.h>

/*!
 * @internal
 * @brief Modify this function to use a different routine than memcpy().
 */
static void* XXH_memcpy(void* dest, const void* src, size_t size)
{
    return memcpy(dest,src,size);
}

#include <limits.h>   /* ULLONG_MAX */


/* *************************************
*  Compiler Specific Options
***************************************/
#ifdef _MSC_VER /* Visual Studio warning fix */
#  pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
#endif

#if XXH_NO_INLINE_HINTS  /* disable inlining hints */
#  if defined(__GNUC__) || defined(__clang__)
#    define XXH_FORCE_INLINE static __attribute__((unused))
#  else
#    define XXH_FORCE_INLINE static
#  endif
#  define XXH_NO_INLINE static
/* enable inlining hints */
#elif defined(__GNUC__) || defined(__clang__)
#  define XXH_FORCE_INLINE static __inline__ __attribute__((always_inline, unused))
#  define XXH_NO_INLINE static __attribute__((noinline))
#elif defined(_MSC_VER)  /* Visual Studio */
#  define XXH_FORCE_INLINE static __forceinline
#  define XXH_NO_INLINE static __declspec(noinline)
#elif defined (__cplusplus) \
  || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L))   /* C99 */
#  define XXH_FORCE_INLINE static inline
#  define XXH_NO_INLINE static
#else
#  define XXH_FORCE_INLINE static
#  define XXH_NO_INLINE static
#endif

#if XXH3_INLINE_SECRET
#  define XXH3_WITH_SECRET_INLINE XXH_FORCE_INLINE
#else
#  define XXH3_WITH_SECRET_INLINE XXH_NO_INLINE
#endif


/* *************************************
*  Debug
***************************************/
/*!
 * @ingroup tuning
 * @def XXH_DEBUGLEVEL
 * @brief Sets the debugging level.
 *
 * XXH_DEBUGLEVEL is expected to be defined externally, typically via the
 * compiler's command line options. The value must be a number.
 */
#ifndef XXH_DEBUGLEVEL
#  ifdef DEBUGLEVEL /* backwards compat */
#    define XXH_DEBUGLEVEL DEBUGLEVEL
#  else
#    define XXH_DEBUGLEVEL 0
#  endif
#endif

#if (XXH_DEBUGLEVEL>=1)
#  include <assert.h>   /* note: can still be disabled with NDEBUG */
#  define XXH_ASSERT(c)   assert(c)
#else
#  if defined(__INTEL_COMPILER)
#    define XXH_ASSERT(c)   XXH_ASSUME((unsigned char) (c))
#  else
#    define XXH_ASSERT(c)   XXH_ASSUME(c)
#  endif
#endif

/* note: use after variable declarations */
#ifndef XXH_STATIC_ASSERT
#  if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)    /* C11 */
#    define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { _Static_assert((c),m); } while(0)
#  elif defined(__cplusplus) && (__cplusplus >= 201103L)            /* C++11 */
#    define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { static_assert((c),m); } while(0)
#  else
#    define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { struct xxh_sa { char x[(c) ? 1 : -1]; }; } while(0)
#  endif
#  define XXH_STATIC_ASSERT(c) XXH_STATIC_ASSERT_WITH_MESSAGE((c),#c)
#endif

/*!
 * @internal
 * @def XXH_COMPILER_GUARD(var)
 * @brief Used to prevent unwanted optimizations for @p var.
 *
 * It uses an empty GCC inline assembly statement with a register constraint
 * which forces @p var into a general purpose register (eg eax, ebx, ecx
 * on x86) and marks it as modified.
 *
 * This is used in a few places to avoid unwanted autovectorization (e.g.
 * XXH32_round()). All vectorization we want is explicit via intrinsics,
 * and _usually_ isn't wanted elsewhere.
 *
 * We also use it to prevent unwanted constant folding for AArch64 in
 * XXH3_initCustomSecret_scalar().
 */
#if defined(__GNUC__) || defined(__clang__)
#  define XXH_COMPILER_GUARD(var) __asm__("" : "+r" (var))
#else
#  define XXH_COMPILER_GUARD(var) ((void)0)
#endif

/* Specifically for NEON vectors which use the "w" constraint, on
 * Clang. */
#if defined(__clang__) && defined(__ARM_ARCH) && !defined(__wasm__)
#  define XXH_COMPILER_GUARD_CLANG_NEON(var) __asm__("" : "+w" (var))
#else
#  define XXH_COMPILER_GUARD_CLANG_NEON(var) ((void)0)
#endif

/* *************************************
*  Basic Types
***************************************/
#if !defined (__VMS) \
 && (defined (__cplusplus) \
 || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
# include <stdint.h>
  typedef uint8_t xxh_u8;
#else
  typedef unsigned char xxh_u8;
#endif
typedef XXH32_hash_t xxh_u32;

#ifdef XXH_OLD_NAMES
#  warning "XXH_OLD_NAMES is planned to be removed starting v0.9. If the program depends on it, consider moving away from it by employing newer type names directly"
#  define BYTE xxh_u8
#  define U8   xxh_u8
#  define U32  xxh_u32
#endif

/* ***   Memory access   *** */

/*!
 * @internal
 * @fn xxh_u32 XXH_read32(const void* ptr)
 * @brief Reads an unaligned 32-bit integer from @p ptr in native endianness.
 *
 * Affected by @ref XXH_FORCE_MEMORY_ACCESS.
 *
 * @param ptr The pointer to read from.
 * @return The 32-bit native endian integer from the bytes at @p ptr.
 */

/*!
 * @internal
 * @fn xxh_u32 XXH_readLE32(const void* ptr)
 * @brief Reads an unaligned 32-bit little endian integer from @p ptr.
 *
 * Affected by @ref XXH_FORCE_MEMORY_ACCESS.
 *
 * @param ptr The pointer to read from.
 * @return The 32-bit little endian integer from the bytes at @p ptr.
 */

/*!
 * @internal
 * @fn xxh_u32 XXH_readBE32(const void* ptr)
 * @brief Reads an unaligned 32-bit big endian integer from @p ptr.
 *
 * Affected by @ref XXH_FORCE_MEMORY_ACCESS.
 *
 * @param ptr The pointer to read from.
 * @return The 32-bit big endian integer from the bytes at @p ptr.
 */

/*!
 * @internal
 * @fn xxh_u32 XXH_readLE32_align(const void* ptr, XXH_alignment align)
 * @brief Like @ref XXH_readLE32(), but has an option for aligned reads.
 *
 * Affected by @ref XXH_FORCE_MEMORY_ACCESS.
 * Note that when @ref XXH_FORCE_ALIGN_CHECK == 0, the @p align parameter is
 * always @ref XXH_alignment::XXH_unaligned.
 *
 * @param ptr The pointer to read from.
 * @param align Whether @p ptr is aligned.
 * @pre
 *   If @p align == @ref XXH_alignment::XXH_aligned, @p ptr must be 4 byte
 *   aligned.
 * @return The 32-bit little endian integer from the bytes at @p ptr.
 */

#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3))
/*
 * Manual byteshift. Best for old compilers which don't inline memcpy.
 * We actually directly use XXH_readLE32 and XXH_readBE32.
 */
#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))

/*
 * Force direct memory access. Only works on CPU which support unaligned memory
 * access in hardware.
 */
static xxh_u32 XXH_read32(const void* memPtr) { return *(const xxh_u32*) memPtr; }

#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))

/*
 * __attribute__((aligned(1))) is supported by gcc and clang. Originally the
 * documentation claimed that it only increased the alignment, but actually it
 * can decrease it on gcc, clang, and icc:
 * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69502,
 * https://gcc.godbolt.org/z/xYez1j67Y.
 */
#ifdef XXH_OLD_NAMES
typedef union { xxh_u32 u32; } __attribute__((packed)) unalign;
#endif
static xxh_u32 XXH_read32(const void* ptr)
{
    typedef __attribute__((aligned(1))) xxh_u32 xxh_unalign32;
    return *((const xxh_unalign32*)ptr);
}

#else

/*
 * Portable and safe solution. Generally efficient.
 * see: https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html
 */
static xxh_u32 XXH_read32(const void* memPtr)
{
    xxh_u32 val;
    XXH_memcpy(&val, memPtr, sizeof(val));
    return val;
}

#endif   /* XXH_FORCE_DIRECT_MEMORY_ACCESS */


/* ***   Endianness   *** */

/*!
 * @ingroup tuning
 * @def XXH_CPU_LITTLE_ENDIAN
 * @brief Whether the target is little endian.
 *
 * Defined to 1 if the target is little endian, or 0 if it is big endian.
 * It can be defined externally, for example on the compiler command line.
 *
 * If it is not defined,
 * a runtime check (which is usually constant folded) is used instead.
 *
 * @note
 *   This is not necessarily defined to an integer constant.
 *
 * @see XXH_isLittleEndian() for the runtime check.
 */
#ifndef XXH_CPU_LITTLE_ENDIAN
/*
 * Try to detect endianness automatically, to avoid the nonstandard behavior
 * in `XXH_isLittleEndian()`
 */
#  if defined(_WIN32) /* Windows is always little endian */ \
     || defined(__LITTLE_ENDIAN__) \
     || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
#    define XXH_CPU_LITTLE_ENDIAN 1
#  elif defined(__BIG_ENDIAN__) \
     || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#    define XXH_CPU_LITTLE_ENDIAN 0
#  else
/*!
 * @internal
 * @brief Runtime check for @ref XXH_CPU_LITTLE_ENDIAN.
 *
 * Most compilers will constant fold this.
 */
static int XXH_isLittleEndian(void)
{
    /*
     * Portable and well-defined behavior.
     * Don't use static: it is detrimental to performance.
     */
    const union { xxh_u32 u; xxh_u8 c[4]; } one = { 1 };
    return one.c[0];
}
#   define XXH_CPU_LITTLE_ENDIAN   XXH_isLittleEndian()
#  endif
#endif




/* ****************************************
*  Compiler-specific Functions and Macros
******************************************/
#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)

#ifdef __has_builtin
#  define XXH_HAS_BUILTIN(x) __has_builtin(x)
#else
#  define XXH_HAS_BUILTIN(x) 0
#endif



/*
 * C23 and future versions have standard "unreachable()".
 * Once it has been implemented reliably we can add it as an
 * additional case:
 *
 * ```
 * #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= XXH_C23_VN)
 * #  include <stddef.h>
 * #  ifdef unreachable
 * #    define XXH_UNREACHABLE() unreachable()
 * #  endif
 * #endif
 * ```
 *
 * Note C++23 also has std::unreachable() which can be detected
 * as follows:
 * ```
 * #if defined(__cpp_lib_unreachable) && (__cpp_lib_unreachable >= 202202L)
 * #  include <utility>
 * #  define XXH_UNREACHABLE() std::unreachable()
 * #endif
 * ```
 * NB: `__cpp_lib_unreachable` is defined in the `<version>` header.
 * We don't use that as including `<utility>` in `extern "C"` blocks
 * doesn't work on GCC12
 */

#if XXH_HAS_BUILTIN(__builtin_unreachable)
#  define XXH_UNREACHABLE() __builtin_unreachable()

#elif defined(_MSC_VER)
#  define XXH_UNREACHABLE() __assume(0)

#else
#  define XXH_UNREACHABLE()
#endif

#if XXH_HAS_BUILTIN(__builtin_assume)
#  define XXH_ASSUME(c) __builtin_assume(c)
#else
#  define XXH_ASSUME(c) if (!(c)) { XXH_UNREACHABLE(); }
#endif

/*!
 * @internal
 * @def XXH_rotl32(x,r)
 * @brief 32-bit rotate left.
 *
 * @param x The 32-bit integer to be rotated.
 * @param r The number of bits to rotate.
 * @pre
 *   @p r > 0 && @p r < 32
 * @note
 *   @p x and @p r may be evaluated multiple times.
 * @return The rotated result.
 */
#if !defined(NO_CLANG_BUILTIN) && XXH_HAS_BUILTIN(__builtin_rotateleft32) \
                               && XXH_HAS_BUILTIN(__builtin_rotateleft64)
#  define XXH_rotl32 __builtin_rotateleft32
#  define XXH_rotl64 __builtin_rotateleft64
/* Note: although _rotl exists for minGW (GCC under windows), performance seems poor */
#elif defined(_MSC_VER)
#  define XXH_rotl32(x,r) _rotl(x,r)
#  define XXH_rotl64(x,r) _rotl64(x,r)
#else
#  define XXH_rotl32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
#  define XXH_rotl64(x,r) (((x) << (r)) | ((x) >> (64 - (r))))
#endif

/*!
 * @internal
 * @fn xxh_u32 XXH_swap32(xxh_u32 x)
 * @brief A 32-bit byteswap.
 *
 * @param x The 32-bit integer to byteswap.
 * @return @p x, byteswapped.
 */
#if defined(_MSC_VER)     /* Visual Studio */
#  define XXH_swap32 _byteswap_ulong
#elif XXH_GCC_VERSION >= 403
#  define XXH_swap32 __builtin_bswap32
#else
static xxh_u32 XXH_swap32 (xxh_u32 x)
{
    return  ((x << 24) & 0xff000000 ) |
            ((x <<  8) & 0x00ff0000 ) |
            ((x >>  8) & 0x0000ff00 ) |
            ((x >> 24) & 0x000000ff );
}
#endif


/* ***************************
*  Memory reads
*****************************/

/*!
 * @internal
 * @brief Enum to indicate whether a pointer is aligned.
 */
typedef enum {
    XXH_aligned,  /*!< Aligned */
    XXH_unaligned /*!< Possibly unaligned */
} XXH_alignment;

/*
 * XXH_FORCE_MEMORY_ACCESS==3 is an endian-independent byteshift load.
 *
 * This is ideal for older compilers which don't inline memcpy.
 */
#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3))

XXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* memPtr)
{
    const xxh_u8* bytePtr = (const xxh_u8 *)memPtr;
    return bytePtr[0]
         | ((xxh_u32)bytePtr[1] << 8)
         | ((xxh_u32)bytePtr[2] << 16)
         | ((xxh_u32)bytePtr[3] << 24);
}

XXH_FORCE_INLINE xxh_u32 XXH_readBE32(const void* memPtr)
{
    const xxh_u8* bytePtr = (const xxh_u8 *)memPtr;
    return bytePtr[3]
         | ((xxh_u32)bytePtr[2] << 8)
         | ((xxh_u32)bytePtr[1] << 16)
         | ((xxh_u32)bytePtr[0] << 24);
}

#else
XXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* ptr)
{
    return XXH_CPU_LITTLE_ENDIAN ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr));
}

static xxh_u32 XXH_readBE32(const void* ptr)
{
    return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr);
}
#endif

XXH_FORCE_INLINE xxh_u32
XXH_readLE32_align(const void* ptr, XXH_alignment align)
{
    if (align==XXH_unaligned) {
        return XXH_readLE32(ptr);
    } else {
        return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u32*)ptr : XXH_swap32(*(const xxh_u32*)ptr);
    }
}


/* *************************************
*  Misc
***************************************/
/*! @ingroup public */
XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; }


/* *******************************************************************
*  32-bit hash functions
*********************************************************************/
/*!
 * @}
 * @defgroup XXH32_impl XXH32 implementation
 * @ingroup impl
 *
 * Details on the XXH32 implementation.
 * @{
 */
 /* #define instead of static const, to be used as initializers */
#define XXH_PRIME32_1  0x9E3779B1U  /*!< 0b10011110001101110111100110110001 */
#define XXH_PRIME32_2  0x85EBCA77U  /*!< 0b10000101111010111100101001110111 */
#define XXH_PRIME32_3  0xC2B2AE3DU  /*!< 0b11000010101100101010111000111101 */
#define XXH_PRIME32_4  0x27D4EB2FU  /*!< 0b00100111110101001110101100101111 */
#define XXH_PRIME32_5  0x165667B1U  /*!< 0b00010110010101100110011110110001 */

#ifdef XXH_OLD_NAMES
#  define PRIME32_1 XXH_PRIME32_1
#  define PRIME32_2 XXH_PRIME32_2
#  define PRIME32_3 XXH_PRIME32_3
#  define PRIME32_4 XXH_PRIME32_4
#  define PRIME32_5 XXH_PRIME32_5
#endif

/*!
 * @internal
 * @brief Normal stripe processing routine.
 *
 * This shuffles the bits so that any bit from @p input impacts several bits in
 * @p acc.
 *
 * @param acc The accumulator lane.
 * @param input The stripe of input to mix.
 * @return The mixed accumulator lane.
 */
static xxh_u32 XXH32_round(xxh_u32 acc, xxh_u32 input)
{
    acc += input * XXH_PRIME32_2;
    acc  = XXH_rotl32(acc, 13);
    acc *= XXH_PRIME32_1;
#if (defined(__SSE4_1__) || defined(__aarch64__) || defined(__wasm_simd128__)) && !defined(XXH_ENABLE_AUTOVECTORIZE)
    /*
     * UGLY HACK:
     * A compiler fence is the only thing that prevents GCC and Clang from
     * autovectorizing the XXH32 loop (pragmas and attributes don't work for some
     * reason) without globally disabling SSE4.1.
     *
     * The reason we want to avoid vectorization is because despite working on
     * 4 integers at a time, there are multiple factors slowing XXH32 down on
     * SSE4:
     * - There's a ridiculous amount of lag from pmulld (10 cycles of latency on
     *   newer chips!) making it slightly slower to multiply four integers at
     *   once compared to four integers independently. Even when pmulld was
     *   fastest, Sandy/Ivy Bridge, it is still not worth it to go into SSE
     *   just to multiply unless doing a long operation.
     *
     * - Four instructions are required to rotate,
     *      movqda tmp,  v // not required with VEX encoding
     *      pslld  tmp, 13 // tmp <<= 13
     *      psrld  v,   19 // x >>= 19
     *      por    v,  tmp // x |= tmp
     *   compared to one for scalar:
     *      roll   v, 13    // reliably fast across the board
     *      shldl  v, v, 13 // Sandy Bridge and later prefer this for some reason
     *
     * - Instruction level parallelism is actually more beneficial here because
     *   the SIMD actually serializes this operation: While v1 is rotating, v2
     *   can load data, while v3 can multiply. SSE forces them to operate
     *   together.
     *
     * This is also enabled on AArch64, as Clang is *very aggressive* in vectorizing
     * the loop. NEON is only faster on the A53, and with the newer cores, it is less
     * than half the speed.
     *
     * Additionally, this is used on WASM SIMD128 because it JITs to the same
     * SIMD instructions and has the same issue.
     */
    XXH_COMPILER_GUARD(acc);
#endif
    return acc;
}

/*!
 * @internal
 * @brief Mixes all bits to finalize the hash.
 *
 * The final mix ensures that all input bits have a chance to impact any bit in
 * the output digest, resulting in an unbiased distribution.
 *
 * @param hash The hash to avalanche.
 * @return The avalanched hash.
 */
static xxh_u32 XXH32_avalanche(xxh_u32 hash)
{
    hash ^= hash >> 15;
    hash *= XXH_PRIME32_2;
    hash ^= hash >> 13;
    hash *= XXH_PRIME32_3;
    hash ^= hash >> 16;
    return hash;
}

#define XXH_get32bits(p) XXH_readLE32_align(p, align)

/*!
 * @internal
 * @brief Processes the last 0-15 bytes of @p ptr.
 *
 * There may be up to 15 bytes remaining to consume from the input.
 * This final stage will digest them to ensure that all input bytes are present
 * in the final mix.
 *
 * @param hash The hash to finalize.
 * @param ptr The pointer to the remaining input.
 * @param len The remaining length, modulo 16.
 * @param align Whether @p ptr is aligned.
 * @return The finalized hash.
 * @see XXH64_finalize().
 */
static XXH_PUREF xxh_u32
XXH32_finalize(xxh_u32 hash, const xxh_u8* ptr, size_t len, XXH_alignment align)
{
#define XXH_PROCESS1 do {                             \
    hash += (*ptr++) * XXH_PRIME32_5;                 \
    hash = XXH_rotl32(hash, 11) * XXH_PRIME32_1;      \
} while (0)

#define XXH_PROCESS4 do {                             \
    hash += XXH_get32bits(ptr) * XXH_PRIME32_3;       \
    ptr += 4;                                         \
    hash  = XXH_rotl32(hash, 17) * XXH_PRIME32_4;     \
} while (0)

    if (ptr==NULL) XXH_ASSERT(len == 0);

    /* Compact rerolled version; generally faster */
    if (!XXH32_ENDJMP) {
        len &= 15;
        while (len >= 4) {
            XXH_PROCESS4;
            len -= 4;
        }
        while (len > 0) {
            XXH_PROCESS1;
            --len;
        }
        return XXH32_avalanche(hash);
    } else {
         switch(len&15) /* or switch(bEnd - p) */ {
           case 12:      XXH_PROCESS4;
                         XXH_FALLTHROUGH;  /* fallthrough */
           case 8:       XXH_PROCESS4;
                         XXH_FALLTHROUGH;  /* fallthrough */
           case 4:       XXH_PROCESS4;
                         return XXH32_avalanche(hash);

           case 13:      XXH_PROCESS4;
                         XXH_FALLTHROUGH;  /* fallthrough */
           case 9:       XXH_PROCESS4;
                         XXH_FALLTHROUGH;  /* fallthrough */
           case 5:       XXH_PROCESS4;
                         XXH_PROCESS1;
                         return XXH32_avalanche(hash);

           case 14:      XXH_PROCESS4;
                         XXH_FALLTHROUGH;  /* fallthrough */
           case 10:      XXH_PROCESS4;
                         XXH_FALLTHROUGH;  /* fallthrough */
           case 6:       XXH_PROCESS4;
                         XXH_PROCESS1;
                         XXH_PROCESS1;
                         return XXH32_avalanche(hash);

           case 15:      XXH_PROCESS4;
                         XXH_FALLTHROUGH;  /* fallthrough */
           case 11:      XXH_PROCESS4;
                         XXH_FALLTHROUGH;  /* fallthrough */
           case 7:       XXH_PROCESS4;
                         XXH_FALLTHROUGH;  /* fallthrough */
           case 3:       XXH_PROCESS1;
                         XXH_FALLTHROUGH;  /* fallthrough */
           case 2:       XXH_PROCESS1;
                         XXH_FALLTHROUGH;  /* fallthrough */
           case 1:       XXH_PROCESS1;
                         XXH_FALLTHROUGH;  /* fallthrough */
           case 0:       return XXH32_avalanche(hash);
        }
        XXH_ASSERT(0);
        return hash;   /* reaching this point is deemed impossible */
    }
}

#ifdef XXH_OLD_NAMES
#  define PROCESS1 XXH_PROCESS1
#  define PROCESS4 XXH_PROCESS4
#else
#  undef XXH_PROCESS1
#  undef XXH_PROCESS4
#endif

/*!
 * @internal
 * @brief The implementation for @ref XXH32().
 *
 * @param input , len , seed Directly passed from @ref XXH32().
 * @param align Whether @p input is aligned.
 * @return The calculated hash.
 */
XXH_FORCE_INLINE XXH_PUREF xxh_u32
XXH32_endian_align(const xxh_u8* input, size_t len, xxh_u32 seed, XXH_alignment align)
{
    xxh_u32 h32;

    if (input==NULL) XXH_ASSERT(len == 0);

    if (len>=16) {
        const xxh_u8* const bEnd = input + len;
        const xxh_u8* const limit = bEnd - 15;
        xxh_u32 v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2;
        xxh_u32 v2 = seed + XXH_PRIME32_2;
        xxh_u32 v3 = seed + 0;
        xxh_u32 v4 = seed - XXH_PRIME32_1;

        do {
            v1 = XXH32_round(v1, XXH_get32bits(input)); input += 4;
            v2 = XXH32_round(v2, XXH_get32bits(input)); input += 4;
            v3 = XXH32_round(v3, XXH_get32bits(input)); input += 4;
            v4 = XXH32_round(v4, XXH_get32bits(input)); input += 4;
        } while (input < limit);

        h32 = XXH_rotl32(v1, 1)  + XXH_rotl32(v2, 7)
            + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);
    } else {
        h32  = seed + XXH_PRIME32_5;
    }

    h32 += (xxh_u32)len;

    return XXH32_finalize(h32, input, len&15, align);
}

/*! @ingroup XXH32_family */
XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t len, XXH32_hash_t seed)
{
#if !defined(XXH_NO_STREAM) && XXH_SIZE_OPT >= 2
    /* Simple version, good for code maintenance, but unfortunately slow for small inputs */
    XXH32_state_t state;
    XXH32_reset(&state, seed);
    XXH32_update(&state, (const xxh_u8*)input, len);
    return XXH32_digest(&state);
#else
    if (XXH_FORCE_ALIGN_CHECK) {
        if ((((size_t)input) & 3) == 0) {   /* Input is 4-bytes aligned, leverage the speed benefit */
            return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_aligned);
    }   }

    return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_unaligned);
#endif
}



/*******   Hash streaming   *******/
#ifndef XXH_NO_STREAM
/*! @ingroup XXH32_family */
XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void)
{
    return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t));
}
/*! @ingroup XXH32_family */
XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr)
{
    XXH_free(statePtr);
    return XXH_OK;
}

/*! @ingroup XXH32_family */
XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState)
{
    XXH_memcpy(dstState, srcState, sizeof(*dstState));
}

/*! @ingroup XXH32_family */
XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, XXH32_hash_t seed)
{
    XXH_ASSERT(statePtr != NULL);
    memset(statePtr, 0, sizeof(*statePtr));
    statePtr->v[0] = seed + XXH_PRIME32_1 + XXH_PRIME32_2;
    statePtr->v[1] = seed + XXH_PRIME32_2;
    statePtr->v[2] = seed + 0;
    statePtr->v[3] = seed - XXH_PRIME32_1;
    return XXH_OK;
}


/*! @ingroup XXH32_family */
XXH_PUBLIC_API XXH_errorcode
XXH32_update(XXH32_state_t* state, const void* input, size_t len)
{
    if (input==NULL) {
        XXH_ASSERT(len == 0);
        return XXH_OK;
    }

    {   const xxh_u8* p = (const xxh_u8*)input;
        const xxh_u8* const bEnd = p + len;

        state->total_len_32 += (XXH32_hash_t)len;
        state->large_len |= (XXH32_hash_t)((len>=16) | (state->total_len_32>=16));

        if (state->memsize + len < 16)  {   /* fill in tmp buffer */
            XXH_memcpy((xxh_u8*)(state->mem32) + state->memsize, input, len);
            state->memsize += (XXH32_hash_t)len;
            return XXH_OK;
        }

        if (state->memsize) {   /* some data left from previous update */
            XXH_memcpy((xxh_u8*)(state->mem32) + state->memsize, input, 16-state->memsize);
            {   const xxh_u32* p32 = state->mem32;
                state->v[0] = XXH32_round(state->v[0], XXH_readLE32(p32)); p32++;
                state->v[1] = XXH32_round(state->v[1], XXH_readLE32(p32)); p32++;
                state->v[2] = XXH32_round(state->v[2], XXH_readLE32(p32)); p32++;
                state->v[3] = XXH32_round(state->v[3], XXH_readLE32(p32));
            }
            p += 16-state->memsize;
            state->memsize = 0;
        }

        if (p <= bEnd-16) {
            const xxh_u8* const limit = bEnd - 16;

            do {
                state->v[0] = XXH32_round(state->v[0], XXH_readLE32(p)); p+=4;
                state->v[1] = XXH32_round(state->v[1], XXH_readLE32(p)); p+=4;
                state->v[2] = XXH32_round(state->v[2], XXH_readLE32(p)); p+=4;
                state->v[3] = XXH32_round(state->v[3], XXH_readLE32(p)); p+=4;
            } while (p<=limit);

        }

        if (p < bEnd) {
            XXH_memcpy(state->mem32, p, (size_t)(bEnd-p));
            state->memsize = (unsigned)(bEnd-p);
        }
    }

    return XXH_OK;
}


/*! @ingroup XXH32_family */
XXH_PUBLIC_API XXH32_hash_t XXH32_digest(const XXH32_state_t* state)
{
    xxh_u32 h32;

    if (state->large_len) {
        h32 = XXH_rotl32(state->v[0], 1)
            + XXH_rotl32(state->v[1], 7)
            + XXH_rotl32(state->v[2], 12)
            + XXH_rotl32(state->v[3], 18);
    } else {
        h32 = state->v[2] /* == seed */ + XXH_PRIME32_5;
    }

    h32 += state->total_len_32;

    return XXH32_finalize(h32, (const xxh_u8*)state->mem32, state->memsize, XXH_aligned);
}
#endif /* !XXH_NO_STREAM */

/*******   Canonical representation   *******/

/*! @ingroup XXH32_family */
XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash)
{
    XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t));
    if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash);
    XXH_memcpy(dst, &hash, sizeof(*dst));
}
/*! @ingroup XXH32_family */
XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src)
{
    return XXH_readBE32(src);
}


#ifndef XXH_NO_LONG_LONG

/* *******************************************************************
*  64-bit hash functions
*********************************************************************/
/*!
 * @}
 * @ingroup impl
 * @{
 */
/*******   Memory access   *******/

typedef XXH64_hash_t xxh_u64;

#ifdef XXH_OLD_NAMES
#  define U64 xxh_u64
#endif

#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3))
/*
 * Manual byteshift. Best for old compilers which don't inline memcpy.
 * We actually directly use XXH_readLE64 and XXH_readBE64.
 */
#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))

/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */
static xxh_u64 XXH_read64(const void* memPtr)
{
    return *(const xxh_u64*) memPtr;
}

#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))

/*
 * __attribute__((aligned(1))) is supported by gcc and clang. Originally the
 * documentation claimed that it only increased the alignment, but actually it
 * can decrease it on gcc, clang, and icc:
 * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69502,
 * https://gcc.godbolt.org/z/xYez1j67Y.
 */
#ifdef XXH_OLD_NAMES
typedef union { xxh_u32 u32; xxh_u64 u64; } __attribute__((packed)) unalign64;
#endif
static xxh_u64 XXH_read64(const void* ptr)
{
    typedef __attribute__((aligned(1))) xxh_u64 xxh_unalign64;
    return *((const xxh_unalign64*)ptr);
}

#else

/*
 * Portable and safe solution. Generally efficient.
 * see: https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html
 */
static xxh_u64 XXH_read64(const void* memPtr)
{
    xxh_u64 val;
    XXH_memcpy(&val, memPtr, sizeof(val));
    return val;
}

#endif   /* XXH_FORCE_DIRECT_MEMORY_ACCESS */

#if defined(_MSC_VER)     /* Visual Studio */
#  define XXH_swap64 _byteswap_uint64
#elif XXH_GCC_VERSION >= 403
#  define XXH_swap64 __builtin_bswap64
#else
static xxh_u64 XXH_swap64(xxh_u64 x)
{
    return  ((x << 56) & 0xff00000000000000ULL) |
            ((x << 40) & 0x00ff000000000000ULL) |
            ((x << 24) & 0x0000ff0000000000ULL) |
            ((x << 8)  & 0x000000ff00000000ULL) |
            ((x >> 8)  & 0x00000000ff000000ULL) |
            ((x >> 24) & 0x0000000000ff0000ULL) |
            ((x >> 40) & 0x000000000000ff00ULL) |
            ((x >> 56) & 0x00000000000000ffULL);
}
#endif


/* XXH_FORCE_MEMORY_ACCESS==3 is an endian-independent byteshift load. */
#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3))

XXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* memPtr)
{
    const xxh_u8* bytePtr = (const xxh_u8 *)memPtr;
    return bytePtr[0]
         | ((xxh_u64)bytePtr[1] << 8)
         | ((xxh_u64)bytePtr[2] << 16)
         | ((xxh_u64)bytePtr[3] << 24)
         | ((xxh_u64)bytePtr[4] << 32)
         | ((xxh_u64)bytePtr[5] << 40)
         | ((xxh_u64)bytePtr[6] << 48)
         | ((xxh_u64)bytePtr[7] << 56);
}

XXH_FORCE_INLINE xxh_u64 XXH_readBE64(const void* memPtr)
{
    const xxh_u8* bytePtr = (const xxh_u8 *)memPtr;
    return bytePtr[7]
         | ((xxh_u64)bytePtr[6] << 8)
         | ((xxh_u64)bytePtr[5] << 16)
         | ((xxh_u64)bytePtr[4] << 24)
         | ((xxh_u64)bytePtr[3] << 32)
         | ((xxh_u64)bytePtr[2] << 40)
         | ((xxh_u64)bytePtr[1] << 48)
         | ((xxh_u64)bytePtr[0] << 56);
}

#else
XXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* ptr)
{
    return XXH_CPU_LITTLE_ENDIAN ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr));
}

static xxh_u64 XXH_readBE64(const void* ptr)
{
    return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr);
}
#endif

XXH_FORCE_INLINE xxh_u64
XXH_readLE64_align(const void* ptr, XXH_alignment align)
{
    if (align==XXH_unaligned)
        return XXH_readLE64(ptr);
    else
        return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u64*)ptr : XXH_swap64(*(const xxh_u64*)ptr);
}


/*******   xxh64   *******/
/*!
 * @}
 * @defgroup XXH64_impl XXH64 implementation
 * @ingroup impl
 *
 * Details on the XXH64 implementation.
 * @{
 */
/* #define rather that static const, to be used as initializers */
#define XXH_PRIME64_1  0x9E3779B185EBCA87ULL  /*!< 0b1001111000110111011110011011000110000101111010111100101010000111 */
#define XXH_PRIME64_2  0xC2B2AE3D27D4EB4FULL  /*!< 0b1100001010110010101011100011110100100111110101001110101101001111 */
#define XXH_PRIME64_3  0x165667B19E3779F9ULL  /*!< 0b0001011001010110011001111011000110011110001101110111100111111001 */
#define XXH_PRIME64_4  0x85EBCA77C2B2AE63ULL  /*!< 0b1000010111101011110010100111011111000010101100101010111001100011 */
#define XXH_PRIME64_5  0x27D4EB2F165667C5ULL  /*!< 0b0010011111010100111010110010111100010110010101100110011111000101 */

#ifdef XXH_OLD_NAMES
#  define PRIME64_1 XXH_PRIME64_1
#  define PRIME64_2 XXH_PRIME64_2
#  define PRIME64_3 XXH_PRIME64_3
#  define PRIME64_4 XXH_PRIME64_4
#  define PRIME64_5 XXH_PRIME64_5
#endif

/*! @copydoc XXH32_round */
static xxh_u64 XXH64_round(xxh_u64 acc, xxh_u64 input)
{
    acc += input * XXH_PRIME64_2;
    acc  = XXH_rotl64(acc, 31);
    acc *= XXH_PRIME64_1;
    return acc;
}

static xxh_u64 XXH64_mergeRound(xxh_u64 acc, xxh_u64 val)
{
    val  = XXH64_round(0, val);
    acc ^= val;
    acc  = acc * XXH_PRIME64_1 + XXH_PRIME64_4;
    return acc;
}

/*! @copydoc XXH32_avalanche */
static xxh_u64 XXH64_avalanche(xxh_u64 hash)
{
    hash ^= hash >> 33;
    hash *= XXH_PRIME64_2;
    hash ^= hash >> 29;
    hash *= XXH_PRIME64_3;
    hash ^= hash >> 32;
    return hash;
}


#define XXH_get64bits(p) XXH_readLE64_align(p, align)

/*!
 * @internal
 * @brief Processes the last 0-31 bytes of @p ptr.
 *
 * There may be up to 31 bytes remaining to consume from the input.
 * This final stage will digest them to ensure that all input bytes are present
 * in the final mix.
 *
 * @param hash The hash to finalize.
 * @param ptr The pointer to the remaining input.
 * @param len The remaining length, modulo 32.
 * @param align Whether @p ptr is aligned.
 * @return The finalized hash
 * @see XXH32_finalize().
 */
static XXH_PUREF xxh_u64
XXH64_finalize(xxh_u64 hash, const xxh_u8* ptr, size_t len, XXH_alignment align)
{
    if (ptr==NULL) XXH_ASSERT(len == 0);
    len &= 31;
    while (len >= 8) {
        xxh_u64 const k1 = XXH64_round(0, XXH_get64bits(ptr));
        ptr += 8;
        hash ^= k1;
        hash  = XXH_rotl64(hash,27) * XXH_PRIME64_1 + XXH_PRIME64_4;
        len -= 8;
    }
    if (len >= 4) {
        hash ^= (xxh_u64)(XXH_get32bits(ptr)) * XXH_PRIME64_1;
        ptr += 4;
        hash = XXH_rotl64(hash, 23) * XXH_PRIME64_2 + XXH_PRIME64_3;
        len -= 4;
    }
    while (len > 0) {
        hash ^= (*ptr++) * XXH_PRIME64_5;
        hash = XXH_rotl64(hash, 11) * XXH_PRIME64_1;
        --len;
    }
    return  XXH64_avalanche(hash);
}

#ifdef XXH_OLD_NAMES
#  define PROCESS1_64 XXH_PROCESS1_64
#  define PROCESS4_64 XXH_PROCESS4_64
#  define PROCESS8_64 XXH_PROCESS8_64
#else
#  undef XXH_PROCESS1_64
#  undef XXH_PROCESS4_64
#  undef XXH_PROCESS8_64
#endif

/*!
 * @internal
 * @brief The implementation for @ref XXH64().
 *
 * @param input , len , seed Directly passed from @ref XXH64().
 * @param align Whether @p input is aligned.
 * @return The calculated hash.
 */
XXH_FORCE_INLINE XXH_PUREF xxh_u64
XXH64_endian_align(const xxh_u8* input, size_t len, xxh_u64 seed, XXH_alignment align)
{
    xxh_u64 h64;
    if (input==NULL) XXH_ASSERT(len == 0);

    if (len>=32) {
        const xxh_u8* const bEnd = input + len;
        const xxh_u8* const limit = bEnd - 31;
        xxh_u64 v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2;
        xxh_u64 v2 = seed + XXH_PRIME64_2;
        xxh_u64 v3 = seed + 0;
        xxh_u64 v4 = seed - XXH_PRIME64_1;

        do {
            v1 = XXH64_round(v1, XXH_get64bits(input)); input+=8;
            v2 = XXH64_round(v2, XXH_get64bits(input)); input+=8;
            v3 = XXH64_round(v3, XXH_get64bits(input)); input+=8;
            v4 = XXH64_round(v4, XXH_get64bits(input)); input+=8;
        } while (input<limit);

        h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
        h64 = XXH64_mergeRound(h64, v1);
        h64 = XXH64_mergeRound(h64, v2);
        h64 = XXH64_mergeRound(h64, v3);
        h64 = XXH64_mergeRound(h64, v4);

    } else {
        h64  = seed + XXH_PRIME64_5;
    }

    h64 += (xxh_u64) len;

    return XXH64_finalize(h64, input, len, align);
}


/*! @ingroup XXH64_family */
XXH_PUBLIC_API XXH64_hash_t XXH64 (XXH_NOESCAPE const void* input, size_t len, XXH64_hash_t seed)
{
#if !defined(XXH_NO_STREAM) && XXH_SIZE_OPT >= 2
    /* Simple version, good for code maintenance, but unfortunately slow for small inputs */
    XXH64_state_t state;
    XXH64_reset(&state, seed);
    XXH64_update(&state, (const xxh_u8*)input, len);
    return XXH64_digest(&state);
#else
    if (XXH_FORCE_ALIGN_CHECK) {
        if ((((size_t)input) & 7)==0) {  /* Input is aligned, let's leverage the speed advantage */
            return XXH64_endian_align((const xxh_u8*)input, len, seed, XXH_aligned);
    }   }

    return XXH64_endian_align((const xxh_u8*)input, len, seed, XXH_unaligned);

#endif
}

/*******   Hash Streaming   *******/
#ifndef XXH_NO_STREAM
/*! @ingroup XXH64_family*/
XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void)
{
    return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t));
}
/*! @ingroup XXH64_family */
XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr)
{
    XXH_free(statePtr);
    return XXH_OK;
}

/*! @ingroup XXH64_family */
XXH_PUBLIC_API void XXH64_copyState(XXH_NOESCAPE XXH64_state_t* dstState, const XXH64_state_t* srcState)
{
    XXH_memcpy(dstState, srcState, sizeof(*dstState));
}

/*! @ingroup XXH64_family */
XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH_NOESCAPE XXH64_state_t* statePtr, XXH64_hash_t seed)
{
    XXH_ASSERT(statePtr != NULL);
    memset(statePtr, 0, sizeof(*statePtr));
    statePtr->v[0] = seed + XXH_PRIME64_1 + XXH_PRIME64_2;
    statePtr->v[1] = seed + XXH_PRIME64_2;
    statePtr->v[2] = seed + 0;
    statePtr->v[3] = seed - XXH_PRIME64_1;
    return XXH_OK;
}

/*! @ingroup XXH64_family */
XXH_PUBLIC_API XXH_errorcode
XXH64_update (XXH_NOESCAPE XXH64_state_t* state, XXH_NOESCAPE const void* input, size_t len)
{
    if (input==NULL) {
        XXH_ASSERT(len == 0);
        return XXH_OK;
    }

    {   const xxh_u8* p = (const xxh_u8*)input;
        const xxh_u8* const bEnd = p + len;

        state->total_len += len;

        if (state->memsize + len < 32) {  /* fill in tmp buffer */
            XXH_memcpy(((xxh_u8*)state->mem64) + state->memsize, input, len);
            state->memsize += (xxh_u32)len;
            return XXH_OK;
        }

        if (state->memsize) {   /* tmp buffer is full */
            XXH_memcpy(((xxh_u8*)state->mem64) + state->memsize, input, 32-state->memsize);
            state->v[0] = XXH64_round(state->v[0], XXH_readLE64(state->mem64+0));
            state->v[1] = XXH64_round(state->v[1], XXH_readLE64(state->mem64+1));
            state->v[2] = XXH64_round(state->v[2], XXH_readLE64(state->mem64+2));
            state->v[3] = XXH64_round(state->v[3], XXH_readLE64(state->mem64+3));
            p += 32 - state->memsize;
            state->memsize = 0;
        }

        if (p+32 <= bEnd) {
            const xxh_u8* const limit = bEnd - 32;

            do {
                state->v[0] = XXH64_round(state->v[0], XXH_readLE64(p)); p+=8;
                state->v[1] = XXH64_round(state->v[1], XXH_readLE64(p)); p+=8;
                state->v[2] = XXH64_round(state->v[2], XXH_readLE64(p)); p+=8;
                state->v[3] = XXH64_round(state->v[3], XXH_readLE64(p)); p+=8;
            } while (p<=limit);

        }

        if (p < bEnd) {
            XXH_memcpy(state->mem64, p, (size_t)(bEnd-p));
            state->memsize = (unsigned)(bEnd-p);
        }
    }

    return XXH_OK;
}


/*! @ingroup XXH64_family */
XXH_PUBLIC_API XXH64_hash_t XXH64_digest(XXH_NOESCAPE const XXH64_state_t* state)
{
    xxh_u64 h64;

    if (state->total_len >= 32) {
        h64 = XXH_rotl64(state->v[0], 1) + XXH_rotl64(state->v[1], 7) + XXH_rotl64(state->v[2], 12) + XXH_rotl64(state->v[3], 18);
        h64 = XXH64_mergeRound(h64, state->v[0]);
        h64 = XXH64_mergeRound(h64, state->v[1]);
        h64 = XXH64_mergeRound(h64, state->v[2]);
        h64 = XXH64_mergeRound(h64, state->v[3]);
    } else {
        h64  = state->v[2] /*seed*/ + XXH_PRIME64_5;
    }

    h64 += (xxh_u64) state->total_len;

    return XXH64_finalize(h64, (const xxh_u8*)state->mem64, (size_t)state->total_len, XXH_aligned);
}
#endif /* !XXH_NO_STREAM */

/******* Canonical representation   *******/

/*! @ingroup XXH64_family */
XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH_NOESCAPE XXH64_canonical_t* dst, XXH64_hash_t hash)
{
    XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t));
    if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash);
    XXH_memcpy(dst, &hash, sizeof(*dst));
}

/*! @ingroup XXH64_family */
XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(XXH_NOESCAPE const XXH64_canonical_t* src)
{
    return XXH_readBE64(src);
}

#ifndef XXH_NO_XXH3

/* *********************************************************************
*  XXH3
*  New generation hash designed for speed on small keys and vectorization
************************************************************************ */
/*!
 * @}
 * @defgroup XXH3_impl XXH3 implementation
 * @ingroup impl
 * @{
 */

/* ===   Compiler specifics   === */

#if ((defined(sun) || defined(__sun)) && __cplusplus) /* Solaris includes __STDC_VERSION__ with C++. Tested with GCC 5.5 */
#  define XXH_RESTRICT   /* disable */
#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* >= C99 */
#  define XXH_RESTRICT   restrict
#elif (defined (__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) \
   || (defined (__clang__)) \
   || (defined (_MSC_VER) && (_MSC_VER >= 1400)) \
   || (defined (__INTEL_COMPILER) && (__INTEL_COMPILER >= 1300))
/*
 * There are a LOT more compilers that recognize __restrict but this
 * covers the major ones.
 */
#  define XXH_RESTRICT   __restrict
#else
#  define XXH_RESTRICT   /* disable */
#endif

#if (defined(__GNUC__) && (__GNUC__ >= 3))  \
  || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) \
  || defined(__clang__)
#    define XXH_likely(x) __builtin_expect(x, 1)
#    define XXH_unlikely(x) __builtin_expect(x, 0)
#else
#    define XXH_likely(x) (x)
#    define XXH_unlikely(x) (x)
#endif

#ifndef XXH_HAS_INCLUDE
#  ifdef __has_include
/*
 * Not defined as XXH_HAS_INCLUDE(x) (function-like) because
 * this causes segfaults in Apple Clang 4.2 (on Mac OS X 10.7 Lion)
 */
#    define XXH_HAS_INCLUDE __has_include
#  else
#    define XXH_HAS_INCLUDE(x) 0
#  endif
#endif

#if defined(__GNUC__) || defined(__clang__)
#  if defined(__ARM_FEATURE_SVE)
#    include <arm_sve.h>
#  endif
#  if defined(__ARM_NEON__) || defined(__ARM_NEON) \
   || (defined(_M_ARM) && _M_ARM >= 7) \
   || defined(_M_ARM64) || defined(_M_ARM64EC) \
   || (defined(__wasm_simd128__) && XXH_HAS_INCLUDE(<arm_neon.h>)) /* WASM SIMD128 via SIMDe */
#    define inline __inline__  /* circumvent a clang bug */
#    include <arm_neon.h>
#    undef inline
#  elif defined(__AVX2__)
#    include <immintrin.h>
#  elif defined(__SSE2__)
#    include <emmintrin.h>
#  endif
#endif

#if defined(_MSC_VER)
#  include <intrin.h>
#endif

/*
 * One goal of XXH3 is to make it fast on both 32-bit and 64-bit, while
 * remaining a true 64-bit/128-bit hash function.
 *
 * This is done by prioritizing a subset of 64-bit operations that can be
 * emulated without too many steps on the average 32-bit machine.
 *
 * For example, these two lines seem similar, and run equally fast on 64-bit:
 *
 *   xxh_u64 x;
 *   x ^= (x >> 47); // good
 *   x ^= (x >> 13); // bad
 *
 * However, to a 32-bit machine, there is a major difference.
 *
 * x ^= (x >> 47) looks like this:
 *
 *   x.lo ^= (x.hi >> (47 - 32));
 *
 * while x ^= (x >> 13) looks like this:
 *
 *   // note: funnel shifts are not usually cheap.
 *   x.lo ^= (x.lo >> 13) | (x.hi << (32 - 13));
 *   x.hi ^= (x.hi >> 13);
 *
 * The first one is significantly faster than the second, simply because the
 * shift is larger than 32. This means:
 *  - All the bits we need are in the upper 32 bits, so we can ignore the lower
 *    32 bits in the shift.
 *  - The shift result will always fit in the lower 32 bits, and therefore,
 *    we can ignore the upper 32 bits in the xor.
 *
 * Thanks to this optimization, XXH3 only requires these features to be efficient:
 *
 *  - Usable unaligned access
 *  - A 32-bit or 64-bit ALU
 *      - If 32-bit, a decent ADC instruction
 *  - A 32 or 64-bit multiply with a 64-bit result
 *  - For the 128-bit variant, a decent byteswap helps short inputs.
 *
 * The first two are already required by XXH32, and almost all 32-bit and 64-bit
 * platforms which can run XXH32 can run XXH3 efficiently.
 *
 * Thumb-1, the classic 16-bit only subset of ARM's instruction set, is one
 * notable exception.
 *
 * First of all, Thumb-1 lacks support for the UMULL instruction which
 * performs the important long multiply. This means numerous __aeabi_lmul
 * calls.
 *
 * Second of all, the 8 functional registers are just not enough.
 * Setup for __aeabi_lmul, byteshift loads, pointers, and all arithmetic need
 * Lo registers, and this shuffling results in thousands more MOVs than A32.
 *
 * A32 and T32 don't have this limitation. They can access all 14 registers,
 * do a 32->64 multiply with UMULL, and the flexible operand allowing free
 * shifts is helpful, too.
 *
 * Therefore, we do a quick sanity check.
 *
 * If compiling Thumb-1 for a target which supports ARM instructions, we will
 * emit a warning, as it is not a "sane" platform to compile for.
 *
 * Usually, if this happens, it is because of an accident and you probably need
 * to specify -march, as you likely meant to compile for a newer architecture.
 *
 * Credit: large sections of the vectorial and asm source code paths
 *         have been contributed by @easyaspi314
 */
#if defined(__thumb__) && !defined(__thumb2__) && defined(__ARM_ARCH_ISA_ARM)
#   warning "XXH3 is highly inefficient without ARM or Thumb-2."
#endif

/* ==========================================
 * Vectorization detection
 * ========================================== */

#ifdef XXH_DOXYGEN
/*!
 * @ingroup tuning
 * @brief Overrides the vectorization implementation chosen for XXH3.
 *
 * Can be defined to 0 to disable SIMD or any of the values mentioned in
 * @ref XXH_VECTOR_TYPE.
 *
 * If this is not defined, it uses predefined macros to determine the best
 * implementation.
 */
#  define XXH_VECTOR XXH_SCALAR
/*!
 * @ingroup tuning
 * @brief Possible values for @ref XXH_VECTOR.
 *
 * Note that these are actually implemented as macros.
 *
 * If this is not defined, it is detected automatically.
 * internal macro XXH_X86DISPATCH overrides this.
 */
enum XXH_VECTOR_TYPE /* fake enum */ {
    XXH_SCALAR = 0,  /*!< Portable scalar version */
    XXH_SSE2   = 1,  /*!<
                      * SSE2 for Pentium 4, Opteron, all x86_64.
                      *
                      * @note SSE2 is also guaranteed on Windows 10, macOS, and
                      * Android x86.
                      */
    XXH_AVX2   = 2,  /*!< AVX2 for Haswell and Bulldozer */
    XXH_AVX512 = 3,  /*!< AVX512 for Skylake and Icelake */
    XXH_NEON   = 4,  /*!<
                       * NEON for most ARMv7-A, all AArch64, and WASM SIMD128
                       * via the SIMDeverywhere polyfill provided with the
                       * Emscripten SDK.
                       */
    XXH_VSX    = 5,  /*!< VSX and ZVector for POWER8/z13 (64-bit) */
    XXH_SVE    = 6,  /*!< SVE for some ARMv8-A and ARMv9-A */
};
/*!
 * @ingroup tuning
 * @brief Selects the minimum alignment for XXH3's accumulators.
 *
 * When using SIMD, this should match the alignment required for said vector
 * type, so, for example, 32 for AVX2.
 *
 * Default: Auto detected.
 */
#  define XXH_ACC_ALIGN 8
#endif

/* Actual definition */
#ifndef XXH_DOXYGEN
#  define XXH_SCALAR 0
#  define XXH_SSE2   1
#  define XXH_AVX2   2
#  define XXH_AVX512 3
#  define XXH_NEON   4
#  define XXH_VSX    5
#  define XXH_SVE    6
#endif

#ifndef XXH_VECTOR    /* can be defined on command line */
#  if defined(__ARM_FEATURE_SVE)
#    define XXH_VECTOR XXH_SVE
#  elif ( \
        defined(__ARM_NEON__) || defined(__ARM_NEON) /* gcc */ \
     || defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC) /* msvc */ \
     || (defined(__wasm_simd128__) && XXH_HAS_INCLUDE(<arm_neon.h>)) /* wasm simd128 via SIMDe */ \
   ) && ( \
        defined(_WIN32) || defined(__LITTLE_ENDIAN__) /* little endian only */ \
    || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) \
   )
#    define XXH_VECTOR XXH_NEON
#  elif defined(__AVX512F__)
#    define XXH_VECTOR XXH_AVX512
#  elif defined(__AVX2__)
#    define XXH_VECTOR XXH_AVX2
#  elif defined(__SSE2__) || defined(_M_AMD64) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP == 2))
#    define XXH_VECTOR XXH_SSE2
#  elif (defined(__PPC64__) && defined(__POWER8_VECTOR__)) \
     || (defined(__s390x__) && defined(__VEC__)) \
     && defined(__GNUC__) /* TODO: IBM XL */
#    define XXH_VECTOR XXH_VSX
#  else
#    define XXH_VECTOR XXH_SCALAR
#  endif
#endif

/* __ARM_FEATURE_SVE is only supported by GCC & Clang. */
#if (XXH_VECTOR == XXH_SVE) && !defined(__ARM_FEATURE_SVE)
#  ifdef _MSC_VER
#    pragma warning(once : 4606)
#  else
#    warning "__ARM_FEATURE_SVE isn't supported. Use SCALAR instead."
#  endif
#  undef XXH_VECTOR
#  define XXH_VECTOR XXH_SCALAR
#endif

/*
 * Controls the alignment of the accumulator,
 * for compatibility with aligned vector loads, which are usually faster.
 */
#ifndef XXH_ACC_ALIGN
#  if defined(XXH_X86DISPATCH)
#     define XXH_ACC_ALIGN 64  /* for compatibility with avx512 */
#  elif XXH_VECTOR == XXH_SCALAR  /* scalar */
#     define XXH_ACC_ALIGN 8
#  elif XXH_VECTOR == XXH_SSE2  /* sse2 */
#     define XXH_ACC_ALIGN 16
#  elif XXH_VECTOR == XXH_AVX2  /* avx2 */
#     define XXH_ACC_ALIGN 32
#  elif XXH_VECTOR == XXH_NEON  /* neon */
#     define XXH_ACC_ALIGN 16
#  elif XXH_VECTOR == XXH_VSX   /* vsx */
#     define XXH_ACC_ALIGN 16
#  elif XXH_VECTOR == XXH_AVX512  /* avx512 */
#     define XXH_ACC_ALIGN 64
#  elif XXH_VECTOR == XXH_SVE   /* sve */
#     define XXH_ACC_ALIGN 64
#  endif
#endif

#if defined(XXH_X86DISPATCH) || XXH_VECTOR == XXH_SSE2 \
    || XXH_VECTOR == XXH_AVX2 || XXH_VECTOR == XXH_AVX512
#  define XXH_SEC_ALIGN XXH_ACC_ALIGN
#elif XXH_VECTOR == XXH_SVE
#  define XXH_SEC_ALIGN XXH_ACC_ALIGN
#else
#  define XXH_SEC_ALIGN 8
#endif

#if defined(__GNUC__) || defined(__clang__)
#  define XXH_ALIASING __attribute__((may_alias))
#else
#  define XXH_ALIASING /* nothing */
#endif

/*
 * UGLY HACK:
 * GCC usually generates the best code with -O3 for xxHash.
 *
 * However, when targeting AVX2, it is overzealous in its unrolling resulting
 * in code roughly 3/4 the speed of Clang.
 *
 * There are other issues, such as GCC splitting _mm256_loadu_si256 into
 * _mm_loadu_si128 + _mm256_inserti128_si256. This is an optimization which
 * only applies to Sandy and Ivy Bridge... which don't even support AVX2.
 *
 * That is why when compiling the AVX2 version, it is recommended to use either
 *   -O2 -mavx2 -march=haswell
 * or
 *   -O2 -mavx2 -mno-avx256-split-unaligned-load
 * for decent performance, or to use Clang instead.
 *
 * Fortunately, we can control the first one with a pragma that forces GCC into
 * -O2, but the other one we can't control without "failed to inline always
 * inline function due to target mismatch" warnings.
 */
#if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \
  && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \
  && defined(__OPTIMIZE__) && XXH_SIZE_OPT <= 0 /* respect -O0 and -Os */
#  pragma GCC push_options
#  pragma GCC optimize("-O2")
#endif

#if XXH_VECTOR == XXH_NEON

/*
 * UGLY HACK: While AArch64 GCC on Linux does not seem to care, on macOS, GCC -O3
 * optimizes out the entire hashLong loop because of the aliasing violation.
 *
 * However, GCC is also inefficient at load-store optimization with vld1q/vst1q,
 * so the only option is to mark it as aliasing.
 */
typedef uint64x2_t xxh_aliasing_uint64x2_t XXH_ALIASING;

/*!
 * @internal
 * @brief `vld1q_u64` but faster and alignment-safe.
 *
 * On AArch64, unaligned access is always safe, but on ARMv7-a, it is only
 * *conditionally* safe (`vld1` has an alignment bit like `movdq[ua]` in x86).
 *
 * GCC for AArch64 sees `vld1q_u8` as an intrinsic instead of a load, so it
 * prohibits load-store optimizations. Therefore, a direct dereference is used.
 *
 * Otherwise, `vld1q_u8` is used with `vreinterpretq_u8_u64` to do a safe
 * unaligned load.
 */
#if defined(__aarch64__) && defined(__GNUC__) && !defined(__clang__)
XXH_FORCE_INLINE uint64x2_t XXH_vld1q_u64(void const* ptr) /* silence -Wcast-align */
{
    return *(xxh_aliasing_uint64x2_t const *)ptr;
}
#else
XXH_FORCE_INLINE uint64x2_t XXH_vld1q_u64(void const* ptr)
{
    return vreinterpretq_u64_u8(vld1q_u8((uint8_t const*)ptr));
}
#endif

/*!
 * @internal
 * @brief `vmlal_u32` on low and high halves of a vector.
 *
 * This is a workaround for AArch64 GCC < 11 which implemented arm_neon.h with
 * inline assembly and were therefore incapable of merging the `vget_{low, high}_u32`
 * with `vmlal_u32`.
 */
#if defined(__aarch64__) && defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 11
XXH_FORCE_INLINE uint64x2_t
XXH_vmlal_low_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs)
{
    /* Inline assembly is the only way */
    __asm__("umlal   %0.2d, %1.2s, %2.2s" : "+w" (acc) : "w" (lhs), "w" (rhs));
    return acc;
}
XXH_FORCE_INLINE uint64x2_t
XXH_vmlal_high_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs)
{
    /* This intrinsic works as expected */
    return vmlal_high_u32(acc, lhs, rhs);
}
#else
/* Portable intrinsic versions */
XXH_FORCE_INLINE uint64x2_t
XXH_vmlal_low_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs)
{
    return vmlal_u32(acc, vget_low_u32(lhs), vget_low_u32(rhs));
}
/*! @copydoc XXH_vmlal_low_u32
 * Assume the compiler converts this to vmlal_high_u32 on aarch64 */
XXH_FORCE_INLINE uint64x2_t
XXH_vmlal_high_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs)
{
    return vmlal_u32(acc, vget_high_u32(lhs), vget_high_u32(rhs));
}
#endif

/*!
 * @ingroup tuning
 * @brief Controls the NEON to scalar ratio for XXH3
 *
 * This can be set to 2, 4, 6, or 8.
 *
 * ARM Cortex CPUs are _very_ sensitive to how their pipelines are used.
 *
 * For example, the Cortex-A73 can dispatch 3 micro-ops per cycle, but only 2 of those
 * can be NEON. If you are only using NEON instructions, you are only using 2/3 of the CPU
 * bandwidth.
 *
 * This is even more noticeable on the more advanced cores like the Cortex-A76 which
 * can dispatch 8 micro-ops per cycle, but still only 2 NEON micro-ops at once.
 *
 * Therefore, to make the most out of the pipeline, it is beneficial to run 6 NEON lanes
 * and 2 scalar lanes, which is chosen by default.
 *
 * This does not apply to Apple processors or 32-bit processors, which run better with
 * full NEON. These will default to 8. Additionally, size-optimized builds run 8 lanes.
 *
 * This change benefits CPUs with large micro-op buffers without negatively affecting
 * most other CPUs:
 *
 *  | Chipset               | Dispatch type       | NEON only | 6:2 hybrid | Diff. |
 *  |:----------------------|:--------------------|----------:|-----------:|------:|
 *  | Snapdragon 730 (A76)  | 2 NEON/8 micro-ops  |  8.8 GB/s |  10.1 GB/s |  ~16% |
 *  | Snapdragon 835 (A73)  | 2 NEON/3 micro-ops  |  5.1 GB/s |   5.3 GB/s |   ~5% |
 *  | Marvell PXA1928 (A53) | In-order dual-issue |  1.9 GB/s |   1.9 GB/s |    0% |
 *  | Apple M1              | 4 NEON/8 micro-ops  | 37.3 GB/s |  36.1 GB/s |  ~-3% |
 *
 * It also seems to fix some bad codegen on GCC, making it almost as fast as clang.
 *
 * When using WASM SIMD128, if this is 2 or 6, SIMDe will scalarize 2 of the lanes meaning
 * it effectively becomes worse 4.
 *
 * @see XXH3_accumulate_512_neon()
 */
# ifndef XXH3_NEON_LANES
#  if (defined(__aarch64__) || defined(__arm64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) \
   && !defined(__APPLE__) && XXH_SIZE_OPT <= 0
#   define XXH3_NEON_LANES 6
#  else
#   define XXH3_NEON_LANES XXH_ACC_NB
#  endif
# endif
#endif  /* XXH_VECTOR == XXH_NEON */

/*
 * VSX and Z Vector helpers.
 *
 * This is very messy, and any pull requests to clean this up are welcome.
 *
 * There are a lot of problems with supporting VSX and s390x, due to
 * inconsistent intrinsics, spotty coverage, and multiple endiannesses.
 */
#if XXH_VECTOR == XXH_VSX
/* Annoyingly, these headers _may_ define three macros: `bool`, `vector`,
 * and `pixel`. This is a problem for obvious reasons.
 *
 * These keywords are unnecessary; the spec literally says they are
 * equivalent to `__bool`, `__vector`, and `__pixel` and may be undef'd
 * after including the header.
 *
 * We use pragma push_macro/pop_macro to keep the namespace clean. */
#  pragma push_macro("bool")
#  pragma push_macro("vector")
#  pragma push_macro("pixel")
/* silence potential macro redefined warnings */
#  undef bool
#  undef vector
#  undef pixel

#  if defined(__s390x__)
#    include <s390intrin.h>
#  else
#    include <altivec.h>
#  endif

/* Restore the original macro values, if applicable. */
#  pragma pop_macro("pixel")
#  pragma pop_macro("vector")
#  pragma pop_macro("bool")

typedef __vector unsigned long long xxh_u64x2;
typedef __vector unsigned char xxh_u8x16;
typedef __vector unsigned xxh_u32x4;

/*
 * UGLY HACK: Similar to aarch64 macOS GCC, s390x GCC has the same aliasing issue.
 */
typedef xxh_u64x2 xxh_aliasing_u64x2 XXH_ALIASING;

# ifndef XXH_VSX_BE
#  if defined(__BIG_ENDIAN__) \
  || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#    define XXH_VSX_BE 1
#  elif defined(__VEC_ELEMENT_REG_ORDER__) && __VEC_ELEMENT_REG_ORDER__ == __ORDER_BIG_ENDIAN__
#    warning "-maltivec=be is not recommended. Please use native endianness."
#    define XXH_VSX_BE 1
#  else
#    define XXH_VSX_BE 0
#  endif
# endif /* !defined(XXH_VSX_BE) */

# if XXH_VSX_BE
#  if defined(__POWER9_VECTOR__) || (defined(__clang__) && defined(__s390x__))
#    define XXH_vec_revb vec_revb
#  else
/*!
 * A polyfill for POWER9's vec_revb().
 */
XXH_FORCE_INLINE xxh_u64x2 XXH_vec_revb(xxh_u64x2 val)
{
    xxh_u8x16 const vByteSwap = { 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
                                  0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08 };
    return vec_perm(val, val, vByteSwap);
}
#  endif
# endif /* XXH_VSX_BE */

/*!
 * Performs an unaligned vector load and byte swaps it on big endian.
 */
XXH_FORCE_INLINE xxh_u64x2 XXH_vec_loadu(const void *ptr)
{
    xxh_u64x2 ret;
    XXH_memcpy(&ret, ptr, sizeof(xxh_u64x2));
# if XXH_VSX_BE
    ret = XXH_vec_revb(ret);
# endif
    return ret;
}

/*
 * vec_mulo and vec_mule are very problematic intrinsics on PowerPC
 *
 * These intrinsics weren't added until GCC 8, despite existing for a while,
 * and they are endian dependent. Also, their meaning swap depending on version.
 * */
# if defined(__s390x__)
 /* s390x is always big endian, no issue on this platform */
#  define XXH_vec_mulo vec_mulo
#  define XXH_vec_mule vec_mule
# elif defined(__clang__) && XXH_HAS_BUILTIN(__builtin_altivec_vmuleuw) && !defined(__ibmxl__)
/* Clang has a better way to control this, we can just use the builtin which doesn't swap. */
 /* The IBM XL Compiler (which defined __clang__) only implements the vec_* operations */
#  define XXH_vec_mulo __builtin_altivec_vmulouw
#  define XXH_vec_mule __builtin_altivec_vmuleuw
# else
/* gcc needs inline assembly */
/* Adapted from https://github.com/google/highwayhash/blob/master/highwayhash/hh_vsx.h. */
XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mulo(xxh_u32x4 a, xxh_u32x4 b)
{
    xxh_u64x2 result;
    __asm__("vmulouw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b));
    return result;
}
XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mule(xxh_u32x4 a, xxh_u32x4 b)
{
    xxh_u64x2 result;
    __asm__("vmuleuw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b));
    return result;
}
# endif /* XXH_vec_mulo, XXH_vec_mule */
#endif /* XXH_VECTOR == XXH_VSX */

#if XXH_VECTOR == XXH_SVE
#define ACCRND(acc, offset) \
do { \
    svuint64_t input_vec = svld1_u64(mask, xinput + offset);         \
    svuint64_t secret_vec = svld1_u64(mask, xsecret + offset);       \
    svuint64_t mixed = sveor_u64_x(mask, secret_vec, input_vec);     \
    svuint64_t swapped = svtbl_u64(input_vec, kSwap);                \
    svuint64_t mixed_lo = svextw_u64_x(mask, mixed);                 \
    svuint64_t mixed_hi = svlsr_n_u64_x(mask, mixed, 32);            \
    svuint64_t mul = svmad_u64_x(mask, mixed_lo, mixed_hi, swapped); \
    acc = svadd_u64_x(mask, acc, mul);                               \
} while (0)
#endif /* XXH_VECTOR == XXH_SVE */

/* prefetch
 * can be disabled, by declaring XXH_NO_PREFETCH build macro */
#if defined(XXH_NO_PREFETCH)
#  define XXH_PREFETCH(ptr)  (void)(ptr)  /* disabled */
#else
#  if XXH_SIZE_OPT >= 1
#    define XXH_PREFETCH(ptr) (void)(ptr)
#  elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))  /* _mm_prefetch() not defined outside of x86/x64 */
#    include <mmintrin.h>   /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */
#    define XXH_PREFETCH(ptr)  _mm_prefetch((const char*)(ptr), _MM_HINT_T0)
#  elif defined(__GNUC__) && ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) )
#    define XXH_PREFETCH(ptr)  __builtin_prefetch((ptr), 0 /* rw==read */, 3 /* locality */)
#  else
#    define XXH_PREFETCH(ptr) (void)(ptr)  /* disabled */
#  endif
#endif  /* XXH_NO_PREFETCH */


/* ==========================================
 * XXH3 default settings
 * ========================================== */

#define XXH_SECRET_DEFAULT_SIZE 192   /* minimum XXH3_SECRET_SIZE_MIN */

#if (XXH_SECRET_DEFAULT_SIZE < XXH3_SECRET_SIZE_MIN)
#  error "default keyset is not large enough"
#endif

/*! Pseudorandom secret taken directly from FARSH. */
XXH_ALIGN(64) static const xxh_u8 XXH3_kSecret[XXH_SECRET_DEFAULT_SIZE] = {
    0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c,
    0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f,
    0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21,
    0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c,
    0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3,
    0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8,
    0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d,
    0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64,
    0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb,
    0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e,
    0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce,
    0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e,
};

static const xxh_u64 PRIME_MX1 = 0x165667919E3779F9ULL;  /*!< 0b0001011001010110011001111001000110011110001101110111100111111001 */
static const xxh_u64 PRIME_MX2 = 0x9FB21C651E98DF25ULL;  /*!< 0b1001111110110010000111000110010100011110100110001101111100100101 */

#ifdef XXH_OLD_NAMES
#  define kSecret XXH3_kSecret
#endif

#ifdef XXH_DOXYGEN
/*!
 * @brief Calculates a 32-bit to 64-bit long multiply.
 *
 * Implemented as a macro.
 *
 * Wraps `__emulu` on MSVC x86 because it tends to call `__allmul` when it doesn't
 * need to (but it shouldn't need to anyways, it is about 7 instructions to do
 * a 64x64 multiply...). Since we know that this will _always_ emit `MULL`, we
 * use that instead of the normal method.
 *
 * If you are compiling for platforms like Thumb-1 and don't have a better option,
 * you may also want to write your own long multiply routine here.
 *
 * @param x, y Numbers to be multiplied
 * @return 64-bit product of the low 32 bits of @p x and @p y.
 */
XXH_FORCE_INLINE xxh_u64
XXH_mult32to64(xxh_u64 x, xxh_u64 y)
{
   return (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF);
}
#elif defined(_MSC_VER) && defined(_M_IX86)
#    define XXH_mult32to64(x, y) __emulu((unsigned)(x), (unsigned)(y))
#else
/*
 * Downcast + upcast is usually better than masking on older compilers like
 * GCC 4.2 (especially 32-bit ones), all without affecting newer compilers.
 *
 * The other method, (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF), will AND both operands
 * and perform a full 64x64 multiply -- entirely redundant on 32-bit.
 */
#    define XXH_mult32to64(x, y) ((xxh_u64)(xxh_u32)(x) * (xxh_u64)(xxh_u32)(y))
#endif

/*!
 * @brief Calculates a 64->128-bit long multiply.
 *
 * Uses `__uint128_t` and `_umul128` if available, otherwise uses a scalar
 * version.
 *
 * @param lhs , rhs The 64-bit integers to be multiplied
 * @return The 128-bit result represented in an @ref XXH128_hash_t.
 */
static XXH128_hash_t
XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs)
{
    /*
     * GCC/Clang __uint128_t method.
     *
     * On most 64-bit targets, GCC and Clang define a __uint128_t type.
     * This is usually the best way as it usually uses a native long 64-bit
     * multiply, such as MULQ on x86_64 or MUL + UMULH on aarch64.
     *
     * Usually.
     *
     * Despite being a 32-bit platform, Clang (and emscripten) define this type
     * despite not having the arithmetic for it. This results in a laggy
     * compiler builtin call which calculates a full 128-bit multiply.
     * In that case it is best to use the portable one.
     * https://github.com/Cyan4973/xxHash/issues/211#issuecomment-515575677
     */
#if (defined(__GNUC__) || defined(__clang__)) && !defined(__wasm__) \
    && defined(__SIZEOF_INT128__) \
    || (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128)

    __uint128_t const product = (__uint128_t)lhs * (__uint128_t)rhs;
    XXH128_hash_t r128;
    r128.low64  = (xxh_u64)(product);
    r128.high64 = (xxh_u64)(product >> 64);
    return r128;

    /*
     * MSVC for x64's _umul128 method.
     *
     * xxh_u64 _umul128(xxh_u64 Multiplier, xxh_u64 Multiplicand, xxh_u64 *HighProduct);
     *
     * This compiles to single operand MUL on x64.
     */
#elif (defined(_M_X64) || defined(_M_IA64)) && !defined(_M_ARM64EC)

#ifndef _MSC_VER
#   pragma intrinsic(_umul128)
#endif
    xxh_u64 product_high;
    xxh_u64 const product_low = _umul128(lhs, rhs, &product_high);
    XXH128_hash_t r128;
    r128.low64  = product_low;
    r128.high64 = product_high;
    return r128;

    /*
     * MSVC for ARM64's __umulh method.
     *
     * This compiles to the same MUL + UMULH as GCC/Clang's __uint128_t method.
     */
#elif defined(_M_ARM64) || defined(_M_ARM64EC)

#ifndef _MSC_VER
#   pragma intrinsic(__umulh)
#endif
    XXH128_hash_t r128;
    r128.low64  = lhs * rhs;
    r128.high64 = __umulh(lhs, rhs);
    return r128;

#else
    /*
     * Portable scalar method. Optimized for 32-bit and 64-bit ALUs.
     *
     * This is a fast and simple grade school multiply, which is shown below
     * with base 10 arithmetic instead of base 0x100000000.
     *
     *           9 3 // D2 lhs = 93
     *         x 7 5 // D2 rhs = 75
     *     ----------
     *           1 5 // D2 lo_lo = (93 % 10) * (75 % 10) = 15
     *         4 5 | // D2 hi_lo = (93 / 10) * (75 % 10) = 45
     *         2 1 | // D2 lo_hi = (93 % 10) * (75 / 10) = 21
     *     + 6 3 | | // D2 hi_hi = (93 / 10) * (75 / 10) = 63
     *     ---------
     *         2 7 | // D2 cross = (15 / 10) + (45 % 10) + 21 = 27
     *     + 6 7 | | // D2 upper = (27 / 10) + (45 / 10) + 63 = 67
     *     ---------
     *       6 9 7 5 // D4 res = (27 * 10) + (15 % 10) + (67 * 100) = 6975
     *
     * The reasons for adding the products like this are:
     *  1. It avoids manual carry tracking. Just like how
     *     (9 * 9) + 9 + 9 = 99, the same applies with this for UINT64_MAX.
     *     This avoids a lot of complexity.
     *
     *  2. It hints for, and on Clang, compiles to, the powerful UMAAL
     *     instruction available in ARM's Digital Signal Processing extension
     *     in 32-bit ARMv6 and later, which is shown below:
     *
     *         void UMAAL(xxh_u32 *RdLo, xxh_u32 *RdHi, xxh_u32 Rn, xxh_u32 Rm)
     *         {
     *             xxh_u64 product = (xxh_u64)*RdLo * (xxh_u64)*RdHi + Rn + Rm;
     *             *RdLo = (xxh_u32)(product & 0xFFFFFFFF);
     *             *RdHi = (xxh_u32)(product >> 32);
     *         }
     *
     *     This instruction was designed for efficient long multiplication, and
     *     allows this to be calculated in only 4 instructions at speeds
     *     comparable to some 64-bit ALUs.
     *
     *  3. It isn't terrible on other platforms. Usually this will be a couple
     *     of 32-bit ADD/ADCs.
     */

    /* First calculate all of the cross products. */
    xxh_u64 const lo_lo = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs & 0xFFFFFFFF);
    xxh_u64 const hi_lo = XXH_mult32to64(lhs >> 32,        rhs & 0xFFFFFFFF);
    xxh_u64 const lo_hi = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs >> 32);
    xxh_u64 const hi_hi = XXH_mult32to64(lhs >> 32,        rhs >> 32);

    /* Now add the products together. These will never overflow. */
    xxh_u64 const cross = (lo_lo >> 32) + (hi_lo & 0xFFFFFFFF) + lo_hi;
    xxh_u64 const upper = (hi_lo >> 32) + (cross >> 32)        + hi_hi;
    xxh_u64 const lower = (cross << 32) | (lo_lo & 0xFFFFFFFF);

    XXH128_hash_t r128;
    r128.low64  = lower;
    r128.high64 = upper;
    return r128;
#endif
}

/*!
 * @brief Calculates a 64-bit to 128-bit multiply, then XOR folds it.
 *
 * The reason for the separate function is to prevent passing too many structs
 * around by value. This will hopefully inline the multiply, but we don't force it.
 *
 * @param lhs , rhs The 64-bit integers to multiply
 * @return The low 64 bits of the product XOR'd by the high 64 bits.
 * @see XXH_mult64to128()
 */
static xxh_u64
XXH3_mul128_fold64(xxh_u64 lhs, xxh_u64 rhs)
{
    XXH128_hash_t product = XXH_mult64to128(lhs, rhs);
    return product.low64 ^ product.high64;
}

/*! Seems to produce slightly better code on GCC for some reason. */
XXH_FORCE_INLINE XXH_CONSTF xxh_u64 XXH_xorshift64(xxh_u64 v64, int shift)
{
    XXH_ASSERT(0 <= shift && shift < 64);
    return v64 ^ (v64 >> shift);
}

/*
 * This is a fast avalanche stage,
 * suitable when input bits are already partially mixed
 */
static XXH64_hash_t XXH3_avalanche(xxh_u64 h64)
{
    h64 = XXH_xorshift64(h64, 37);
    h64 *= PRIME_MX1;
    h64 = XXH_xorshift64(h64, 32);
    return h64;
}

/*
 * This is a stronger avalanche,
 * inspired by Pelle Evensen's rrmxmx
 * preferable when input has not been previously mixed
 */
static XXH64_hash_t XXH3_rrmxmx(xxh_u64 h64, xxh_u64 len)
{
    /* this mix is inspired by Pelle Evensen's rrmxmx */
    h64 ^= XXH_rotl64(h64, 49) ^ XXH_rotl64(h64, 24);
    h64 *= PRIME_MX2;
    h64 ^= (h64 >> 35) + len ;
    h64 *= PRIME_MX2;
    return XXH_xorshift64(h64, 28);
}


/* ==========================================
 * Short keys
 * ==========================================
 * One of the shortcomings of XXH32 and XXH64 was that their performance was
 * sub-optimal on short lengths. It used an iterative algorithm which strongly
 * favored lengths that were a multiple of 4 or 8.
 *
 * Instead of iterating over individual inputs, we use a set of single shot
 * functions which piece together a range of lengths and operate in constant time.
 *
 * Additionally, the number of multiplies has been significantly reduced. This
 * reduces latency, especially when emulating 64-bit multiplies on 32-bit.
 *
 * Depending on the platform, this may or may not be faster than XXH32, but it
 * is almost guaranteed to be faster than XXH64.
 */

/*
 * At very short lengths, there isn't enough input to fully hide secrets, or use
 * the entire secret.
 *
 * There is also only a limited amount of mixing we can do before significantly
 * impacting performance.
 *
 * Therefore, we use different sections of the secret and always mix two secret
 * samples with an XOR. This should have no effect on performance on the
 * seedless or withSeed variants because everything _should_ be constant folded
 * by modern compilers.
 *
 * The XOR mixing hides individual parts of the secret and increases entropy.
 *
 * This adds an extra layer of strength for custom secrets.
 */
XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t
XXH3_len_1to3_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
{
    XXH_ASSERT(input != NULL);
    XXH_ASSERT(1 <= len && len <= 3);
    XXH_ASSERT(secret != NULL);
    /*
     * len = 1: combined = { input[0], 0x01, input[0], input[0] }
     * len = 2: combined = { input[1], 0x02, input[0], input[1] }
     * len = 3: combined = { input[2], 0x03, input[0], input[1] }
     */
    {   xxh_u8  const c1 = input[0];
        xxh_u8  const c2 = input[len >> 1];
        xxh_u8  const c3 = input[len - 1];
        xxh_u32 const combined = ((xxh_u32)c1 << 16) | ((xxh_u32)c2  << 24)
                               | ((xxh_u32)c3 <<  0) | ((xxh_u32)len << 8);
        xxh_u64 const bitflip = (XXH_readLE32(secret) ^ XXH_readLE32(secret+4)) + seed;
        xxh_u64 const keyed = (xxh_u64)combined ^ bitflip;
        return XXH64_avalanche(keyed);
    }
}

XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t
XXH3_len_4to8_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
{
    XXH_ASSERT(input != NULL);
    XXH_ASSERT(secret != NULL);
    XXH_ASSERT(4 <= len && len <= 8);
    seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32;
    {   xxh_u32 const input1 = XXH_readLE32(input);
        xxh_u32 const input2 = XXH_readLE32(input + len - 4);
        xxh_u64 const bitflip = (XXH_readLE64(secret+8) ^ XXH_readLE64(secret+16)) - seed;
        xxh_u64 const input64 = input2 + (((xxh_u64)input1) << 32);
        xxh_u64 const keyed = input64 ^ bitflip;
        return XXH3_rrmxmx(keyed, len);
    }
}

XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t
XXH3_len_9to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
{
    XXH_ASSERT(input != NULL);
    XXH_ASSERT(secret != NULL);
    XXH_ASSERT(9 <= len && len <= 16);
    {   xxh_u64 const bitflip1 = (XXH_readLE64(secret+24) ^ XXH_readLE64(secret+32)) + seed;
        xxh_u64 const bitflip2 = (XXH_readLE64(secret+40) ^ XXH_readLE64(secret+48)) - seed;
        xxh_u64 const input_lo = XXH_readLE64(input)           ^ bitflip1;
        xxh_u64 const input_hi = XXH_readLE64(input + len - 8) ^ bitflip2;
        xxh_u64 const acc = len
                          + XXH_swap64(input_lo) + input_hi
                          + XXH3_mul128_fold64(input_lo, input_hi);
        return XXH3_avalanche(acc);
    }
}

XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t
XXH3_len_0to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
{
    XXH_ASSERT(len <= 16);
    {   if (XXH_likely(len >  8)) return XXH3_len_9to16_64b(input, len, secret, seed);
        if (XXH_likely(len >= 4)) return XXH3_len_4to8_64b(input, len, secret, seed);
        if (len) return XXH3_len_1to3_64b(input, len, secret, seed);
        return XXH64_avalanche(seed ^ (XXH_readLE64(secret+56) ^ XXH_readLE64(secret+64)));
    }
}

/*
 * DISCLAIMER: There are known *seed-dependent* multicollisions here due to
 * multiplication by zero, affecting hashes of lengths 17 to 240.
 *
 * However, they are very unlikely.
 *
 * Keep this in mind when using the unseeded XXH3_64bits() variant: As with all
 * unseeded non-cryptographic hashes, it does not attempt to defend itself
 * against specially crafted inputs, only random inputs.
 *
 * Compared to classic UMAC where a 1 in 2^31 chance of 4 consecutive bytes
 * cancelling out the secret is taken an arbitrary number of times (addressed
 * in XXH3_accumulate_512), this collision is very unlikely with random inputs
 * and/or proper seeding:
 *
 * This only has a 1 in 2^63 chance of 8 consecutive bytes cancelling out, in a
 * function that is only called up to 16 times per hash with up to 240 bytes of
 * input.
 *
 * This is not too bad for a non-cryptographic hash function, especially with
 * only 64 bit outputs.
 *
 * The 128-bit variant (which trades some speed for strength) is NOT affected
 * by this, although it is always a good idea to use a proper seed if you care
 * about strength.
 */
XXH_FORCE_INLINE xxh_u64 XXH3_mix16B(const xxh_u8* XXH_RESTRICT input,
                                     const xxh_u8* XXH_RESTRICT secret, xxh_u64 seed64)
{
#if defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \
  && defined(__i386__) && defined(__SSE2__)  /* x86 + SSE2 */ \
  && !defined(XXH_ENABLE_AUTOVECTORIZE)      /* Define to disable like XXH32 hack */
    /*
     * UGLY HACK:
     * GCC for x86 tends to autovectorize the 128-bit multiply, resulting in
     * slower code.
     *
     * By forcing seed64 into a register, we disrupt the cost model and
     * cause it to scalarize. See `XXH32_round()`
     *
     * FIXME: Clang's output is still _much_ faster -- On an AMD Ryzen 3600,
     * XXH3_64bits @ len=240 runs at 4.6 GB/s with Clang 9, but 3.3 GB/s on
     * GCC 9.2, despite both emitting scalar code.
     *
     * GCC generates much better scalar code than Clang for the rest of XXH3,
     * which is why finding a more optimal codepath is an interest.
     */
    XXH_COMPILER_GUARD(seed64);
#endif
    {   xxh_u64 const input_lo = XXH_readLE64(input);
        xxh_u64 const input_hi = XXH_readLE64(input+8);
        return XXH3_mul128_fold64(
            input_lo ^ (XXH_readLE64(secret)   + seed64),
            input_hi ^ (XXH_readLE64(secret+8) - seed64)
        );
    }
}

/* For mid range keys, XXH3 uses a Mum-hash variant. */
XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t
XXH3_len_17to128_64b(const xxh_u8* XXH_RESTRICT input, size_t len,
                     const xxh_u8* XXH_RESTRICT secret, size_t secretSize,
                     XXH64_hash_t seed)
{
    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;
    XXH_ASSERT(16 < len && len <= 128);

    {   xxh_u64 acc = len * XXH_PRIME64_1;
#if XXH_SIZE_OPT >= 1
        /* Smaller and cleaner, but slightly slower. */
        unsigned int i = (unsigned int)(len - 1) / 32;
        do {
            acc += XXH3_mix16B(input+16 * i, secret+32*i, seed);
            acc += XXH3_mix16B(input+len-16*(i+1), secret+32*i+16, seed);
        } while (i-- != 0);
#else
        if (len > 32) {
            if (len > 64) {
                if (len > 96) {
                    acc += XXH3_mix16B(input+48, secret+96, seed);
                    acc += XXH3_mix16B(input+len-64, secret+112, seed);
                }
                acc += XXH3_mix16B(input+32, secret+64, seed);
                acc += XXH3_mix16B(input+len-48, secret+80, seed);
            }
            acc += XXH3_mix16B(input+16, secret+32, seed);
            acc += XXH3_mix16B(input+len-32, secret+48, seed);
        }
        acc += XXH3_mix16B(input+0, secret+0, seed);
        acc += XXH3_mix16B(input+len-16, secret+16, seed);
#endif
        return XXH3_avalanche(acc);
    }
}

/*!
 * @brief Maximum size of "short" key in bytes.
 */
#define XXH3_MIDSIZE_MAX 240

XXH_NO_INLINE XXH_PUREF XXH64_hash_t
XXH3_len_129to240_64b(const xxh_u8* XXH_RESTRICT input, size_t len,
                      const xxh_u8* XXH_RESTRICT secret, size_t secretSize,
                      XXH64_hash_t seed)
{
    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;
    XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX);

    #define XXH3_MIDSIZE_STARTOFFSET 3
    #define XXH3_MIDSIZE_LASTOFFSET  17

    {   xxh_u64 acc = len * XXH_PRIME64_1;
        xxh_u64 acc_end;
        unsigned int const nbRounds = (unsigned int)len / 16;
        unsigned int i;
        XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX);
        for (i=0; i<8; i++) {
            acc += XXH3_mix16B(input+(16*i), secret+(16*i), seed);
        }
        /* last bytes */
        acc_end = XXH3_mix16B(input + len - 16, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET, seed);
        XXH_ASSERT(nbRounds >= 8);
        acc = XXH3_avalanche(acc);
#if defined(__clang__)                                /* Clang */ \
    && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \
    && !defined(XXH_ENABLE_AUTOVECTORIZE)             /* Define to disable */
        /*
         * UGLY HACK:
         * Clang for ARMv7-A tries to vectorize this loop, similar to GCC x86.
         * In everywhere else, it uses scalar code.
         *
         * For 64->128-bit multiplies, even if the NEON was 100% optimal, it
         * would still be slower than UMAAL (see XXH_mult64to128).
         *
         * Unfortunately, Clang doesn't handle the long multiplies properly and
         * converts them to the nonexistent "vmulq_u64" intrinsic, which is then
         * scalarized into an ugly mess of VMOV.32 instructions.
         *
         * This mess is difficult to avoid without turning autovectorization
         * off completely, but they are usually relatively minor and/or not
         * worth it to fix.
         *
         * This loop is the easiest to fix, as unlike XXH32, this pragma
         * _actually works_ because it is a loop vectorization instead of an
         * SLP vectorization.
         */
        #pragma clang loop vectorize(disable)
#endif
        for (i=8 ; i < nbRounds; i++) {
            /*
             * Prevents clang for unrolling the acc loop and interleaving with this one.
             */
            XXH_COMPILER_GUARD(acc);
            acc_end += XXH3_mix16B(input+(16*i), secret+(16*(i-8)) + XXH3_MIDSIZE_STARTOFFSET, seed);
        }
        return XXH3_avalanche(acc + acc_end);
    }
}


/* =======     Long Keys     ======= */

#define XXH_STRIPE_LEN 64
#define XXH_SECRET_CONSUME_RATE 8   /* nb of secret bytes consumed at each accumulation */
#define XXH_ACC_NB (XXH_STRIPE_LEN / sizeof(xxh_u64))

#ifdef XXH_OLD_NAMES
#  define STRIPE_LEN XXH_STRIPE_LEN
#  define ACC_NB XXH_ACC_NB
#endif

#ifndef XXH_PREFETCH_DIST
#  ifdef __clang__
#    define XXH_PREFETCH_DIST 320
#  else
#    if (XXH_VECTOR == XXH_AVX512)
#      define XXH_PREFETCH_DIST 512
#    else
#      define XXH_PREFETCH_DIST 384
#    endif
#  endif  /* __clang__ */
#endif  /* XXH_PREFETCH_DIST */

/*
 * These macros are to generate an XXH3_accumulate() function.
 * The two arguments select the name suffix and target attribute.
 *
 * The name of this symbol is XXH3_accumulate_<name>() and it calls
 * XXH3_accumulate_512_<name>().
 *
 * It may be useful to hand implement this function if the compiler fails to
 * optimize the inline function.
 */
#define XXH3_ACCUMULATE_TEMPLATE(name)                      \
void                                                        \
XXH3_accumulate_##name(xxh_u64* XXH_RESTRICT acc,           \
                       const xxh_u8* XXH_RESTRICT input,    \
                       const xxh_u8* XXH_RESTRICT secret,   \
                       size_t nbStripes)                    \
{                                                           \
    size_t n;                                               \
    for (n = 0; n < nbStripes; n++ ) {                      \
        const xxh_u8* const in = input + n*XXH_STRIPE_LEN;  \
        XXH_PREFETCH(in + XXH_PREFETCH_DIST);               \
        XXH3_accumulate_512_##name(                         \
                 acc,                                       \
                 in,                                        \
                 secret + n*XXH_SECRET_CONSUME_RATE);       \
    }                                                       \
}


XXH_FORCE_INLINE void XXH_writeLE64(void* dst, xxh_u64 v64)
{
    if (!XXH_CPU_LITTLE_ENDIAN) v64 = XXH_swap64(v64);
    XXH_memcpy(dst, &v64, sizeof(v64));
}

/* Several intrinsic functions below are supposed to accept __int64 as argument,
 * as documented in https://software.intel.com/sites/landingpage/IntrinsicsGuide/ .
 * However, several environments do not define __int64 type,
 * requiring a workaround.
 */
#if !defined (__VMS) \
  && (defined (__cplusplus) \
  || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
    typedef int64_t xxh_i64;
#else
    /* the following type must have a width of 64-bit */
    typedef long long xxh_i64;
#endif


/*
 * XXH3_accumulate_512 is the tightest loop for long inputs, and it is the most optimized.
 *
 * It is a hardened version of UMAC, based off of FARSH's implementation.
 *
 * This was chosen because it adapts quite well to 32-bit, 64-bit, and SIMD
 * implementations, and it is ridiculously fast.
 *
 * We harden it by mixing the original input to the accumulators as well as the product.
 *
 * This means that in the (relatively likely) case of a multiply by zero, the
 * original input is preserved.
 *
 * On 128-bit inputs, we swap 64-bit pairs when we add the input to improve
 * cross-pollination, as otherwise the upper and lower halves would be
 * essentially independent.
 *
 * This doesn't matter on 64-bit hashes since they all get merged together in
 * the end, so we skip the extra step.
 *
 * Both XXH3_64bits and XXH3_128bits use this subroutine.
 */

#if (XXH_VECTOR == XXH_AVX512) \
     || (defined(XXH_DISPATCH_AVX512) && XXH_DISPATCH_AVX512 != 0)

#ifndef XXH_TARGET_AVX512
# define XXH_TARGET_AVX512  /* disable attribute target */
#endif

XXH_FORCE_INLINE XXH_TARGET_AVX512 void
XXH3_accumulate_512_avx512(void* XXH_RESTRICT acc,
                     const void* XXH_RESTRICT input,
                     const void* XXH_RESTRICT secret)
{
    __m512i* const xacc = (__m512i *) acc;
    XXH_ASSERT((((size_t)acc) & 63) == 0);
    XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i));

    {
        /* data_vec    = input[0]; */
        __m512i const data_vec    = _mm512_loadu_si512   (input);
        /* key_vec     = secret[0]; */
        __m512i const key_vec     = _mm512_loadu_si512   (secret);
        /* data_key    = data_vec ^ key_vec; */
        __m512i const data_key    = _mm512_xor_si512     (data_vec, key_vec);
        /* data_key_lo = data_key >> 32; */
        __m512i const data_key_lo = _mm512_srli_epi64 (data_key, 32);
        /* product     = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */
        __m512i const product     = _mm512_mul_epu32     (data_key, data_key_lo);
        /* xacc[0] += swap(data_vec); */
        __m512i const data_swap = _mm512_shuffle_epi32(data_vec, (_MM_PERM_ENUM)_MM_SHUFFLE(1, 0, 3, 2));
        __m512i const sum       = _mm512_add_epi64(*xacc, data_swap);
        /* xacc[0] += product; */
        *xacc = _mm512_add_epi64(product, sum);
    }
}
XXH_FORCE_INLINE XXH_TARGET_AVX512 XXH3_ACCUMULATE_TEMPLATE(avx512)

/*
 * XXH3_scrambleAcc: Scrambles the accumulators to improve mixing.
 *
 * Multiplication isn't perfect, as explained by Google in HighwayHash:
 *
 *  // Multiplication mixes/scrambles bytes 0-7 of the 64-bit result to
 *  // varying degrees. In descending order of goodness, bytes
 *  // 3 4 2 5 1 6 0 7 have quality 228 224 164 160 100 96 36 32.
 *  // As expected, the upper and lower bytes are much worse.
 *
 * Source: https://github.com/google/highwayhash/blob/0aaf66b/highwayhash/hh_avx2.h#L291
 *
 * Since our algorithm uses a pseudorandom secret to add some variance into the
 * mix, we don't need to (or want to) mix as often or as much as HighwayHash does.
 *
 * This isn't as tight as XXH3_accumulate, but still written in SIMD to avoid
 * extraction.
 *
 * Both XXH3_64bits and XXH3_128bits use this subroutine.
 */

XXH_FORCE_INLINE XXH_TARGET_AVX512 void
XXH3_scrambleAcc_avx512(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
{
    XXH_ASSERT((((size_t)acc) & 63) == 0);
    XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i));
    {   __m512i* const xacc = (__m512i*) acc;
        const __m512i prime32 = _mm512_set1_epi32((int)XXH_PRIME32_1);

        /* xacc[0] ^= (xacc[0] >> 47) */
        __m512i const acc_vec     = *xacc;
        __m512i const shifted     = _mm512_srli_epi64    (acc_vec, 47);
        /* xacc[0] ^= secret; */
        __m512i const key_vec     = _mm512_loadu_si512   (secret);
        __m512i const data_key    = _mm512_ternarylogic_epi32(key_vec, acc_vec, shifted, 0x96 /* key_vec ^ acc_vec ^ shifted */);

        /* xacc[0] *= XXH_PRIME32_1; */
        __m512i const data_key_hi = _mm512_srli_epi64 (data_key, 32);
        __m512i const prod_lo     = _mm512_mul_epu32     (data_key, prime32);
        __m512i const prod_hi     = _mm512_mul_epu32     (data_key_hi, prime32);
        *xacc = _mm512_add_epi64(prod_lo, _mm512_slli_epi64(prod_hi, 32));
    }
}

XXH_FORCE_INLINE XXH_TARGET_AVX512 void
XXH3_initCustomSecret_avx512(void* XXH_RESTRICT customSecret, xxh_u64 seed64)
{
    XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 63) == 0);
    XXH_STATIC_ASSERT(XXH_SEC_ALIGN == 64);
    XXH_ASSERT(((size_t)customSecret & 63) == 0);
    (void)(&XXH_writeLE64);
    {   int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m512i);
        __m512i const seed_pos = _mm512_set1_epi64((xxh_i64)seed64);
        __m512i const seed     = _mm512_mask_sub_epi64(seed_pos, 0xAA, _mm512_set1_epi8(0), seed_pos);

        const __m512i* const src  = (const __m512i*) ((const void*) XXH3_kSecret);
              __m512i* const dest = (      __m512i*) customSecret;
        int i;
        XXH_ASSERT(((size_t)src & 63) == 0); /* control alignment */
        XXH_ASSERT(((size_t)dest & 63) == 0);
        for (i=0; i < nbRounds; ++i) {
            dest[i] = _mm512_add_epi64(_mm512_load_si512(src + i), seed);
    }   }
}

#endif

#if (XXH_VECTOR == XXH_AVX2) \
    || (defined(XXH_DISPATCH_AVX2) && XXH_DISPATCH_AVX2 != 0)

#ifndef XXH_TARGET_AVX2
# define XXH_TARGET_AVX2  /* disable attribute target */
#endif

XXH_FORCE_INLINE XXH_TARGET_AVX2 void
XXH3_accumulate_512_avx2( void* XXH_RESTRICT acc,
                    const void* XXH_RESTRICT input,
                    const void* XXH_RESTRICT secret)
{
    XXH_ASSERT((((size_t)acc) & 31) == 0);
    {   __m256i* const xacc    =       (__m256i *) acc;
        /* Unaligned. This is mainly for pointer arithmetic, and because
         * _mm256_loadu_si256 requires  a const __m256i * pointer for some reason. */
        const         __m256i* const xinput  = (const __m256i *) input;
        /* Unaligned. This is mainly for pointer arithmetic, and because
         * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */
        const         __m256i* const xsecret = (const __m256i *) secret;

        size_t i;
        for (i=0; i < XXH_STRIPE_LEN/sizeof(__m256i); i++) {
            /* data_vec    = xinput[i]; */
            __m256i const data_vec    = _mm256_loadu_si256    (xinput+i);
            /* key_vec     = xsecret[i]; */
            __m256i const key_vec     = _mm256_loadu_si256   (xsecret+i);
            /* data_key    = data_vec ^ key_vec; */
            __m256i const data_key    = _mm256_xor_si256     (data_vec, key_vec);
            /* data_key_lo = data_key >> 32; */
            __m256i const data_key_lo = _mm256_srli_epi64 (data_key, 32);
            /* product     = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */
            __m256i const product     = _mm256_mul_epu32     (data_key, data_key_lo);
            /* xacc[i] += swap(data_vec); */
            __m256i const data_swap = _mm256_shuffle_epi32(data_vec, _MM_SHUFFLE(1, 0, 3, 2));
            __m256i const sum       = _mm256_add_epi64(xacc[i], data_swap);
            /* xacc[i] += product; */
            xacc[i] = _mm256_add_epi64(product, sum);
    }   }
}
XXH_FORCE_INLINE XXH_TARGET_AVX2 XXH3_ACCUMULATE_TEMPLATE(avx2)

XXH_FORCE_INLINE XXH_TARGET_AVX2 void
XXH3_scrambleAcc_avx2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
{
    XXH_ASSERT((((size_t)acc) & 31) == 0);
    {   __m256i* const xacc = (__m256i*) acc;
        /* Unaligned. This is mainly for pointer arithmetic, and because
         * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */
        const         __m256i* const xsecret = (const __m256i *) secret;
        const __m256i prime32 = _mm256_set1_epi32((int)XXH_PRIME32_1);

        size_t i;
        for (i=0; i < XXH_STRIPE_LEN/sizeof(__m256i); i++) {
            /* xacc[i] ^= (xacc[i] >> 47) */
            __m256i const acc_vec     = xacc[i];
            __m256i const shifted     = _mm256_srli_epi64    (acc_vec, 47);
            __m256i const data_vec    = _mm256_xor_si256     (acc_vec, shifted);
            /* xacc[i] ^= xsecret; */
            __m256i const key_vec     = _mm256_loadu_si256   (xsecret+i);
            __m256i const data_key    = _mm256_xor_si256     (data_vec, key_vec);

            /* xacc[i] *= XXH_PRIME32_1; */
            __m256i const data_key_hi = _mm256_srli_epi64 (data_key, 32);
            __m256i const prod_lo     = _mm256_mul_epu32     (data_key, prime32);
            __m256i const prod_hi     = _mm256_mul_epu32     (data_key_hi, prime32);
            xacc[i] = _mm256_add_epi64(prod_lo, _mm256_slli_epi64(prod_hi, 32));
        }
    }
}

XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_initCustomSecret_avx2(void* XXH_RESTRICT customSecret, xxh_u64 seed64)
{
    XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 31) == 0);
    XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE / sizeof(__m256i)) == 6);
    XXH_STATIC_ASSERT(XXH_SEC_ALIGN <= 64);
    (void)(&XXH_writeLE64);
    XXH_PREFETCH(customSecret);
    {   __m256i const seed = _mm256_set_epi64x((xxh_i64)(0U - seed64), (xxh_i64)seed64, (xxh_i64)(0U - seed64), (xxh_i64)seed64);

        const __m256i* const src  = (const __m256i*) ((const void*) XXH3_kSecret);
              __m256i*       dest = (      __m256i*) customSecret;

#       if defined(__GNUC__) || defined(__clang__)
        /*
         * On GCC & Clang, marking 'dest' as modified will cause the compiler:
         *   - do not extract the secret from sse registers in the internal loop
         *   - use less common registers, and avoid pushing these reg into stack
         */
        XXH_COMPILER_GUARD(dest);
#       endif
        XXH_ASSERT(((size_t)src & 31) == 0); /* control alignment */
        XXH_ASSERT(((size_t)dest & 31) == 0);

        /* GCC -O2 need unroll loop manually */
        dest[0] = _mm256_add_epi64(_mm256_load_si256(src+0), seed);
        dest[1] = _mm256_add_epi64(_mm256_load_si256(src+1), seed);
        dest[2] = _mm256_add_epi64(_mm256_load_si256(src+2), seed);
        dest[3] = _mm256_add_epi64(_mm256_load_si256(src+3), seed);
        dest[4] = _mm256_add_epi64(_mm256_load_si256(src+4), seed);
        dest[5] = _mm256_add_epi64(_mm256_load_si256(src+5), seed);
    }
}

#endif

/* x86dispatch always generates SSE2 */
#if (XXH_VECTOR == XXH_SSE2) || defined(XXH_X86DISPATCH)

#ifndef XXH_TARGET_SSE2
# define XXH_TARGET_SSE2  /* disable attribute target */
#endif

XXH_FORCE_INLINE XXH_TARGET_SSE2 void
XXH3_accumulate_512_sse2( void* XXH_RESTRICT acc,
                    const void* XXH_RESTRICT input,
                    const void* XXH_RESTRICT secret)
{
    /* SSE2 is just a half-scale version of the AVX2 version. */
    XXH_ASSERT((((size_t)acc) & 15) == 0);
    {   __m128i* const xacc    =       (__m128i *) acc;
        /* Unaligned. This is mainly for pointer arithmetic, and because
         * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */
        const         __m128i* const xinput  = (const __m128i *) input;
        /* Unaligned. This is mainly for pointer arithmetic, and because
         * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */
        const         __m128i* const xsecret = (const __m128i *) secret;

        size_t i;
        for (i=0; i < XXH_STRIPE_LEN/sizeof(__m128i); i++) {
            /* data_vec    = xinput[i]; */
            __m128i const data_vec    = _mm_loadu_si128   (xinput+i);
            /* key_vec     = xsecret[i]; */
            __m128i const key_vec     = _mm_loadu_si128   (xsecret+i);
            /* data_key    = data_vec ^ key_vec; */
            __m128i const data_key    = _mm_xor_si128     (data_vec, key_vec);
            /* data_key_lo = data_key >> 32; */
            __m128i const data_key_lo = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1));
            /* product     = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */
            __m128i const product     = _mm_mul_epu32     (data_key, data_key_lo);
            /* xacc[i] += swap(data_vec); */
            __m128i const data_swap = _mm_shuffle_epi32(data_vec, _MM_SHUFFLE(1,0,3,2));
            __m128i const sum       = _mm_add_epi64(xacc[i], data_swap);
            /* xacc[i] += product; */
            xacc[i] = _mm_add_epi64(product, sum);
    }   }
}
XXH_FORCE_INLINE XXH_TARGET_SSE2 XXH3_ACCUMULATE_TEMPLATE(sse2)

XXH_FORCE_INLINE XXH_TARGET_SSE2 void
XXH3_scrambleAcc_sse2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
{
    XXH_ASSERT((((size_t)acc) & 15) == 0);
    {   __m128i* const xacc = (__m128i*) acc;
        /* Unaligned. This is mainly for pointer arithmetic, and because
         * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */
        const         __m128i* const xsecret = (const __m128i *) secret;
        const __m128i prime32 = _mm_set1_epi32((int)XXH_PRIME32_1);

        size_t i;
        for (i=0; i < XXH_STRIPE_LEN/sizeof(__m128i); i++) {
            /* xacc[i] ^= (xacc[i] >> 47) */
            __m128i const acc_vec     = xacc[i];
            __m128i const shifted     = _mm_srli_epi64    (acc_vec, 47);
            __m128i const data_vec    = _mm_xor_si128     (acc_vec, shifted);
            /* xacc[i] ^= xsecret[i]; */
            __m128i const key_vec     = _mm_loadu_si128   (xsecret+i);
            __m128i const data_key    = _mm_xor_si128     (data_vec, key_vec);

            /* xacc[i] *= XXH_PRIME32_1; */
            __m128i const data_key_hi = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1));
            __m128i const prod_lo     = _mm_mul_epu32     (data_key, prime32);
            __m128i const prod_hi     = _mm_mul_epu32     (data_key_hi, prime32);
            xacc[i] = _mm_add_epi64(prod_lo, _mm_slli_epi64(prod_hi, 32));
        }
    }
}

XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_initCustomSecret_sse2(void* XXH_RESTRICT customSecret, xxh_u64 seed64)
{
    XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0);
    (void)(&XXH_writeLE64);
    {   int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m128i);

#       if defined(_MSC_VER) && defined(_M_IX86) && _MSC_VER < 1900
        /* MSVC 32bit mode does not support _mm_set_epi64x before 2015 */
        XXH_ALIGN(16) const xxh_i64 seed64x2[2] = { (xxh_i64)seed64, (xxh_i64)(0U - seed64) };
        __m128i const seed = _mm_load_si128((__m128i const*)seed64x2);
#       else
        __m128i const seed = _mm_set_epi64x((xxh_i64)(0U - seed64), (xxh_i64)seed64);
#       endif
        int i;

        const void* const src16 = XXH3_kSecret;
        __m128i* dst16 = (__m128i*) customSecret;
#       if defined(__GNUC__) || defined(__clang__)
        /*
         * On GCC & Clang, marking 'dest' as modified will cause the compiler:
         *   - do not extract the secret from sse registers in the internal loop
         *   - use less common registers, and avoid pushing these reg into stack
         */
        XXH_COMPILER_GUARD(dst16);
#       endif
        XXH_ASSERT(((size_t)src16 & 15) == 0); /* control alignment */
        XXH_ASSERT(((size_t)dst16 & 15) == 0);

        for (i=0; i < nbRounds; ++i) {
            dst16[i] = _mm_add_epi64(_mm_load_si128((const __m128i *)src16+i), seed);
    }   }
}

#endif

#if (XXH_VECTOR == XXH_NEON)

/* forward declarations for the scalar routines */
XXH_FORCE_INLINE void
XXH3_scalarRound(void* XXH_RESTRICT acc, void const* XXH_RESTRICT input,
                 void const* XXH_RESTRICT secret, size_t lane);

XXH_FORCE_INLINE void
XXH3_scalarScrambleRound(void* XXH_RESTRICT acc,
                         void const* XXH_RESTRICT secret, size_t lane);

/*!
 * @internal
 * @brief The bulk processing loop for NEON and WASM SIMD128.
 *
 * The NEON code path is actually partially scalar when running on AArch64. This
 * is to optimize the pipelining and can have up to 15% speedup depending on the
 * CPU, and it also mitigates some GCC codegen issues.
 *
 * @see XXH3_NEON_LANES for configuring this and details about this optimization.
 *
 * NEON's 32-bit to 64-bit long multiply takes a half vector of 32-bit
 * integers instead of the other platforms which mask full 64-bit vectors,
 * so the setup is more complicated than just shifting right.
 *
 * Additionally, there is an optimization for 4 lanes at once noted below.
 *
 * Since, as stated, the most optimal amount of lanes for Cortexes is 6,
 * there needs to be *three* versions of the accumulate operation used
 * for the remaining 2 lanes.
 *
 * WASM's SIMD128 uses SIMDe's arm_neon.h polyfill because the intrinsics overlap
 * nearly perfectly.
 */

XXH_FORCE_INLINE void
XXH3_accumulate_512_neon( void* XXH_RESTRICT acc,
                    const void* XXH_RESTRICT input,
                    const void* XXH_RESTRICT secret)
{
    XXH_ASSERT((((size_t)acc) & 15) == 0);
    XXH_STATIC_ASSERT(XXH3_NEON_LANES > 0 && XXH3_NEON_LANES <= XXH_ACC_NB && XXH3_NEON_LANES % 2 == 0);
    {   /* GCC for darwin arm64 does not like aliasing here */
        xxh_aliasing_uint64x2_t* const xacc = (xxh_aliasing_uint64x2_t*) acc;
        /* We don't use a uint32x4_t pointer because it causes bus errors on ARMv7. */
        uint8_t const* xinput = (const uint8_t *) input;
        uint8_t const* xsecret  = (const uint8_t *) secret;

        size_t i;
#ifdef __wasm_simd128__
        /*
         * On WASM SIMD128, Clang emits direct address loads when XXH3_kSecret
         * is constant propagated, which results in it converting it to this
         * inside the loop:
         *
         *    a = v128.load(XXH3_kSecret +  0 + $secret_offset, offset = 0)
         *    b = v128.load(XXH3_kSecret + 16 + $secret_offset, offset = 0)
         *    ...
         *
         * This requires a full 32-bit address immediate (and therefore a 6 byte
         * instruction) as well as an add for each offset.
         *
         * Putting an asm guard prevents it from folding (at the cost of losing
         * the alignment hint), and uses the free offset in `v128.load` instead
         * of adding secret_offset each time which overall reduces code size by
         * about a kilobyte and improves performance.
         */
        XXH_COMPILER_GUARD(xsecret);
#endif
        /* Scalar lanes use the normal scalarRound routine */
        for (i = XXH3_NEON_LANES; i < XXH_ACC_NB; i++) {
            XXH3_scalarRound(acc, input, secret, i);
        }
        i = 0;
        /* 4 NEON lanes at a time. */
        for (; i+1 < XXH3_NEON_LANES / 2; i+=2) {
            /* data_vec = xinput[i]; */
            uint64x2_t data_vec_1 = XXH_vld1q_u64(xinput  + (i * 16));
            uint64x2_t data_vec_2 = XXH_vld1q_u64(xinput  + ((i+1) * 16));
            /* key_vec  = xsecret[i];  */
            uint64x2_t key_vec_1  = XXH_vld1q_u64(xsecret + (i * 16));
            uint64x2_t key_vec_2  = XXH_vld1q_u64(xsecret + ((i+1) * 16));
            /* data_swap = swap(data_vec) */
            uint64x2_t data_swap_1 = vextq_u64(data_vec_1, data_vec_1, 1);
            uint64x2_t data_swap_2 = vextq_u64(data_vec_2, data_vec_2, 1);
            /* data_key = data_vec ^ key_vec; */
            uint64x2_t data_key_1 = veorq_u64(data_vec_1, key_vec_1);
            uint64x2_t data_key_2 = veorq_u64(data_vec_2, key_vec_2);

            /*
             * If we reinterpret the 64x2 vectors as 32x4 vectors, we can use a
             * de-interleave operation for 4 lanes in 1 step with `vuzpq_u32` to
             * get one vector with the low 32 bits of each lane, and one vector
             * with the high 32 bits of each lane.
             *
             * The intrinsic returns a double vector because the original ARMv7-a
             * instruction modified both arguments in place. AArch64 and SIMD128 emit
             * two instructions from this intrinsic.
             *
             *  [ dk11L | dk11H | dk12L | dk12H ] -> [ dk11L | dk12L | dk21L | dk22L ]
             *  [ dk21L | dk21H | dk22L | dk22H ] -> [ dk11H | dk12H | dk21H | dk22H ]
             */
            uint32x4x2_t unzipped = vuzpq_u32(
                vreinterpretq_u32_u64(data_key_1),
                vreinterpretq_u32_u64(data_key_2)
            );
            /* data_key_lo = data_key & 0xFFFFFFFF */
            uint32x4_t data_key_lo = unzipped.val[0];
            /* data_key_hi = data_key >> 32 */
            uint32x4_t data_key_hi = unzipped.val[1];
            /*
             * Then, we can split the vectors horizontally and multiply which, as for most
             * widening intrinsics, have a variant that works on both high half vectors
             * for free on AArch64. A similar instruction is available on SIMD128.
             *
             * sum = data_swap + (u64x2) data_key_lo * (u64x2) data_key_hi
             */
            uint64x2_t sum_1 = XXH_vmlal_low_u32(data_swap_1, data_key_lo, data_key_hi);
            uint64x2_t sum_2 = XXH_vmlal_high_u32(data_swap_2, data_key_lo, data_key_hi);
            /*
             * Clang reorders
             *    a += b * c;     // umlal   swap.2d, dkl.2s, dkh.2s
             *    c += a;         // add     acc.2d, acc.2d, swap.2d
             * to
             *    c += a;         // add     acc.2d, acc.2d, swap.2d
             *    c += b * c;     // umlal   acc.2d, dkl.2s, dkh.2s
             *
             * While it would make sense in theory since the addition is faster,
             * for reasons likely related to umlal being limited to certain NEON
             * pipelines, this is worse. A compiler guard fixes this.
             */
            XXH_COMPILER_GUARD_CLANG_NEON(sum_1);
            XXH_COMPILER_GUARD_CLANG_NEON(sum_2);
            /* xacc[i] = acc_vec + sum; */
            xacc[i]   = vaddq_u64(xacc[i], sum_1);
            xacc[i+1] = vaddq_u64(xacc[i+1], sum_2);
        }
        /* Operate on the remaining NEON lanes 2 at a time. */
        for (; i < XXH3_NEON_LANES / 2; i++) {
            /* data_vec = xinput[i]; */
            uint64x2_t data_vec = XXH_vld1q_u64(xinput  + (i * 16));
            /* key_vec  = xsecret[i];  */
            uint64x2_t key_vec  = XXH_vld1q_u64(xsecret + (i * 16));
            /* acc_vec_2 = swap(data_vec) */
            uint64x2_t data_swap = vextq_u64(data_vec, data_vec, 1);
            /* data_key = data_vec ^ key_vec; */
            uint64x2_t data_key = veorq_u64(data_vec, key_vec);
            /* For two lanes, just use VMOVN and VSHRN. */
            /* data_key_lo = data_key & 0xFFFFFFFF; */
            uint32x2_t data_key_lo = vmovn_u64(data_key);
            /* data_key_hi = data_key >> 32; */
            uint32x2_t data_key_hi = vshrn_n_u64(data_key, 32);
            /* sum = data_swap + (u64x2) data_key_lo * (u64x2) data_key_hi; */
            uint64x2_t sum = vmlal_u32(data_swap, data_key_lo, data_key_hi);
            /* Same Clang workaround as before */
            XXH_COMPILER_GUARD_CLANG_NEON(sum);
            /* xacc[i] = acc_vec + sum; */
            xacc[i] = vaddq_u64 (xacc[i], sum);
        }
    }
}
XXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(neon)

XXH_FORCE_INLINE void
XXH3_scrambleAcc_neon(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
{
    XXH_ASSERT((((size_t)acc) & 15) == 0);

    {   xxh_aliasing_uint64x2_t* xacc       = (xxh_aliasing_uint64x2_t*) acc;
        uint8_t const* xsecret = (uint8_t const*) secret;

        size_t i;
        /* WASM uses operator overloads and doesn't need these. */
#ifndef __wasm_simd128__
        /* { prime32_1, prime32_1 } */
        uint32x2_t const kPrimeLo = vdup_n_u32(XXH_PRIME32_1);
        /* { 0, prime32_1, 0, prime32_1 } */
        uint32x4_t const kPrimeHi = vreinterpretq_u32_u64(vdupq_n_u64((xxh_u64)XXH_PRIME32_1 << 32));
#endif

        /* AArch64 uses both scalar and neon at the same time */
        for (i = XXH3_NEON_LANES; i < XXH_ACC_NB; i++) {
            XXH3_scalarScrambleRound(acc, secret, i);
        }
        for (i=0; i < XXH3_NEON_LANES / 2; i++) {
            /* xacc[i] ^= (xacc[i] >> 47); */
            uint64x2_t acc_vec  = xacc[i];
            uint64x2_t shifted  = vshrq_n_u64(acc_vec, 47);
            uint64x2_t data_vec = veorq_u64(acc_vec, shifted);

            /* xacc[i] ^= xsecret[i]; */
            uint64x2_t key_vec  = XXH_vld1q_u64(xsecret + (i * 16));
            uint64x2_t data_key = veorq_u64(data_vec, key_vec);
            /* xacc[i] *= XXH_PRIME32_1 */
#ifdef __wasm_simd128__
            /* SIMD128 has multiply by u64x2, use it instead of expanding and scalarizing */
            xacc[i] = data_key * XXH_PRIME32_1;
#else
            /*
             * Expanded version with portable NEON intrinsics
             *
             *    lo(x) * lo(y) + (hi(x) * lo(y) << 32)
             *
             * prod_hi = hi(data_key) * lo(prime) << 32
             *
             * Since we only need 32 bits of this multiply a trick can be used, reinterpreting the vector
             * as a uint32x4_t and multiplying by { 0, prime, 0, prime } to cancel out the unwanted bits
             * and avoid the shift.
             */
            uint32x4_t prod_hi = vmulq_u32 (vreinterpretq_u32_u64(data_key), kPrimeHi);
            /* Extract low bits for vmlal_u32  */
            uint32x2_t data_key_lo = vmovn_u64(data_key);
            /* xacc[i] = prod_hi + lo(data_key) * XXH_PRIME32_1; */
            xacc[i] = vmlal_u32(vreinterpretq_u64_u32(prod_hi), data_key_lo, kPrimeLo);
#endif
        }
    }
}
#endif

#if (XXH_VECTOR == XXH_VSX)

XXH_FORCE_INLINE void
XXH3_accumulate_512_vsx(  void* XXH_RESTRICT acc,
                    const void* XXH_RESTRICT input,
                    const void* XXH_RESTRICT secret)
{
    /* presumed aligned */
    xxh_aliasing_u64x2* const xacc = (xxh_aliasing_u64x2*) acc;
    xxh_u8 const* const xinput   = (xxh_u8 const*) input;   /* no alignment restriction */
    xxh_u8 const* const xsecret  = (xxh_u8 const*) secret;    /* no alignment restriction */
    xxh_u64x2 const v32 = { 32, 32 };
    size_t i;
    for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) {
        /* data_vec = xinput[i]; */
        xxh_u64x2 const data_vec = XXH_vec_loadu(xinput + 16*i);
        /* key_vec = xsecret[i]; */
        xxh_u64x2 const key_vec  = XXH_vec_loadu(xsecret + 16*i);
        xxh_u64x2 const data_key = data_vec ^ key_vec;
        /* shuffled = (data_key << 32) | (data_key >> 32); */
        xxh_u32x4 const shuffled = (xxh_u32x4)vec_rl(data_key, v32);
        /* product = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)shuffled & 0xFFFFFFFF); */
        xxh_u64x2 const product  = XXH_vec_mulo((xxh_u32x4)data_key, shuffled);
        /* acc_vec = xacc[i]; */
        xxh_u64x2 acc_vec        = xacc[i];
        acc_vec += product;

        /* swap high and low halves */
#ifdef __s390x__
        acc_vec += vec_permi(data_vec, data_vec, 2);
#else
        acc_vec += vec_xxpermdi(data_vec, data_vec, 2);
#endif
        xacc[i] = acc_vec;
    }
}
XXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(vsx)

XXH_FORCE_INLINE void
XXH3_scrambleAcc_vsx(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
{
    XXH_ASSERT((((size_t)acc) & 15) == 0);

    {   xxh_aliasing_u64x2* const xacc = (xxh_aliasing_u64x2*) acc;
        const xxh_u8* const xsecret = (const xxh_u8*) secret;
        /* constants */
        xxh_u64x2 const v32  = { 32, 32 };
        xxh_u64x2 const v47 = { 47, 47 };
        xxh_u32x4 const prime = { XXH_PRIME32_1, XXH_PRIME32_1, XXH_PRIME32_1, XXH_PRIME32_1 };
        size_t i;
        for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) {
            /* xacc[i] ^= (xacc[i] >> 47); */
            xxh_u64x2 const acc_vec  = xacc[i];
            xxh_u64x2 const data_vec = acc_vec ^ (acc_vec >> v47);

            /* xacc[i] ^= xsecret[i]; */
            xxh_u64x2 const key_vec  = XXH_vec_loadu(xsecret + 16*i);
            xxh_u64x2 const data_key = data_vec ^ key_vec;

            /* xacc[i] *= XXH_PRIME32_1 */
            /* prod_lo = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)prime & 0xFFFFFFFF);  */
            xxh_u64x2 const prod_even  = XXH_vec_mule((xxh_u32x4)data_key, prime);
            /* prod_hi = ((xxh_u64x2)data_key >> 32) * ((xxh_u64x2)prime >> 32);  */
            xxh_u64x2 const prod_odd  = XXH_vec_mulo((xxh_u32x4)data_key, prime);
            xacc[i] = prod_odd + (prod_even << v32);
    }   }
}

#endif

#if (XXH_VECTOR == XXH_SVE)

XXH_FORCE_INLINE void
XXH3_accumulate_512_sve( void* XXH_RESTRICT acc,
                   const void* XXH_RESTRICT input,
                   const void* XXH_RESTRICT secret)
{
    uint64_t *xacc = (uint64_t *)acc;
    const uint64_t *xinput = (const uint64_t *)(const void *)input;
    const uint64_t *xsecret = (const uint64_t *)(const void *)secret;
    svuint64_t kSwap = sveor_n_u64_z(svptrue_b64(), svindex_u64(0, 1), 1);
    uint64_t element_count = svcntd();
    if (element_count >= 8) {
        svbool_t mask = svptrue_pat_b64(SV_VL8);
        svuint64_t vacc = svld1_u64(mask, xacc);
        ACCRND(vacc, 0);
        svst1_u64(mask, xacc, vacc);
    } else if (element_count == 2) {   /* sve128 */
        svbool_t mask = svptrue_pat_b64(SV_VL2);
        svuint64_t acc0 = svld1_u64(mask, xacc + 0);
        svuint64_t acc1 = svld1_u64(mask, xacc + 2);
        svuint64_t acc2 = svld1_u64(mask, xacc + 4);
        svuint64_t acc3 = svld1_u64(mask, xacc + 6);
        ACCRND(acc0, 0);
        ACCRND(acc1, 2);
        ACCRND(acc2, 4);
        ACCRND(acc3, 6);
        svst1_u64(mask, xacc + 0, acc0);
        svst1_u64(mask, xacc + 2, acc1);
        svst1_u64(mask, xacc + 4, acc2);
        svst1_u64(mask, xacc + 6, acc3);
    } else {
        svbool_t mask = svptrue_pat_b64(SV_VL4);
        svuint64_t acc0 = svld1_u64(mask, xacc + 0);
        svuint64_t acc1 = svld1_u64(mask, xacc + 4);
        ACCRND(acc0, 0);
        ACCRND(acc1, 4);
        svst1_u64(mask, xacc + 0, acc0);
        svst1_u64(mask, xacc + 4, acc1);
    }
}

XXH_FORCE_INLINE void
XXH3_accumulate_sve(xxh_u64* XXH_RESTRICT acc,
               const xxh_u8* XXH_RESTRICT input,
               const xxh_u8* XXH_RESTRICT secret,
               size_t nbStripes)
{
    if (nbStripes != 0) {
        uint64_t *xacc = (uint64_t *)acc;
        const uint64_t *xinput = (const uint64_t *)(const void *)input;
        const uint64_t *xsecret = (const uint64_t *)(const void *)secret;
        svuint64_t kSwap = sveor_n_u64_z(svptrue_b64(), svindex_u64(0, 1), 1);
        uint64_t element_count = svcntd();
        if (element_count >= 8) {
            svbool_t mask = svptrue_pat_b64(SV_VL8);
            svuint64_t vacc = svld1_u64(mask, xacc + 0);
            do {
                /* svprfd(svbool_t, void *, enum svfprop); */
                svprfd(mask, xinput + 128, SV_PLDL1STRM);
                ACCRND(vacc, 0);
                xinput += 8;
                xsecret += 1;
                nbStripes--;
           } while (nbStripes != 0);

           svst1_u64(mask, xacc + 0, vacc);
        } else if (element_count == 2) { /* sve128 */
            svbool_t mask = svptrue_pat_b64(SV_VL2);
            svuint64_t acc0 = svld1_u64(mask, xacc + 0);
            svuint64_t acc1 = svld1_u64(mask, xacc + 2);
            svuint64_t acc2 = svld1_u64(mask, xacc + 4);
            svuint64_t acc3 = svld1_u64(mask, xacc + 6);
            do {
                svprfd(mask, xinput + 128, SV_PLDL1STRM);
                ACCRND(acc0, 0);
                ACCRND(acc1, 2);
                ACCRND(acc2, 4);
                ACCRND(acc3, 6);
                xinput += 8;
                xsecret += 1;
                nbStripes--;
           } while (nbStripes != 0);

           svst1_u64(mask, xacc + 0, acc0);
           svst1_u64(mask, xacc + 2, acc1);
           svst1_u64(mask, xacc + 4, acc2);
           svst1_u64(mask, xacc + 6, acc3);
        } else {
            svbool_t mask = svptrue_pat_b64(SV_VL4);
            svuint64_t acc0 = svld1_u64(mask, xacc + 0);
            svuint64_t acc1 = svld1_u64(mask, xacc + 4);
            do {
                svprfd(mask, xinput + 128, SV_PLDL1STRM);
                ACCRND(acc0, 0);
                ACCRND(acc1, 4);
                xinput += 8;
                xsecret += 1;
                nbStripes--;
           } while (nbStripes != 0);

           svst1_u64(mask, xacc + 0, acc0);
           svst1_u64(mask, xacc + 4, acc1);
       }
    }
}

#endif

/* scalar variants - universal */

#if defined(__aarch64__) && (defined(__GNUC__) || defined(__clang__))
/*
 * In XXH3_scalarRound(), GCC and Clang have a similar codegen issue, where they
 * emit an excess mask and a full 64-bit multiply-add (MADD X-form).
 *
 * While this might not seem like much, as AArch64 is a 64-bit architecture, only
 * big Cortex designs have a full 64-bit multiplier.
 *
 * On the little cores, the smaller 32-bit multiplier is used, and full 64-bit
 * multiplies expand to 2-3 multiplies in microcode. This has a major penalty
 * of up to 4 latency cycles and 2 stall cycles in the multiply pipeline.
 *
 * Thankfully, AArch64 still provides the 32-bit long multiply-add (UMADDL) which does
 * not have this penalty and does the mask automatically.
 */
XXH_FORCE_INLINE xxh_u64
XXH_mult32to64_add64(xxh_u64 lhs, xxh_u64 rhs, xxh_u64 acc)
{
    xxh_u64 ret;
    /* note: %x = 64-bit register, %w = 32-bit register */
    __asm__("umaddl %x0, %w1, %w2, %x3" : "=r" (ret) : "r" (lhs), "r" (rhs), "r" (acc));
    return ret;
}
#else
XXH_FORCE_INLINE xxh_u64
XXH_mult32to64_add64(xxh_u64 lhs, xxh_u64 rhs, xxh_u64 acc)
{
    return XXH_mult32to64((xxh_u32)lhs, (xxh_u32)rhs) + acc;
}
#endif

/*!
 * @internal
 * @brief Scalar round for @ref XXH3_accumulate_512_scalar().
 *
 * This is extracted to its own function because the NEON path uses a combination
 * of NEON and scalar.
 */
XXH_FORCE_INLINE void
XXH3_scalarRound(void* XXH_RESTRICT acc,
                 void const* XXH_RESTRICT input,
                 void const* XXH_RESTRICT secret,
                 size_t lane)
{
    xxh_u64* xacc = (xxh_u64*) acc;
    xxh_u8 const* xinput  = (xxh_u8 const*) input;
    xxh_u8 const* xsecret = (xxh_u8 const*) secret;
    XXH_ASSERT(lane < XXH_ACC_NB);
    XXH_ASSERT(((size_t)acc & (XXH_ACC_ALIGN-1)) == 0);
    {
        xxh_u64 const data_val = XXH_readLE64(xinput + lane * 8);
        xxh_u64 const data_key = data_val ^ XXH_readLE64(xsecret + lane * 8);
        xacc[lane ^ 1] += data_val; /* swap adjacent lanes */
        xacc[lane] = XXH_mult32to64_add64(data_key /* & 0xFFFFFFFF */, data_key >> 32, xacc[lane]);
    }
}

/*!
 * @internal
 * @brief Processes a 64 byte block of data using the scalar path.
 */
XXH_FORCE_INLINE void
XXH3_accumulate_512_scalar(void* XXH_RESTRICT acc,
                     const void* XXH_RESTRICT input,
                     const void* XXH_RESTRICT secret)
{
    size_t i;
    /* ARM GCC refuses to unroll this loop, resulting in a 24% slowdown on ARMv6. */
#if defined(__GNUC__) && !defined(__clang__) \
  && (defined(__arm__) || defined(__thumb2__)) \
  && defined(__ARM_FEATURE_UNALIGNED) /* no unaligned access just wastes bytes */ \
  && XXH_SIZE_OPT <= 0
#  pragma GCC unroll 8
#endif
    for (i=0; i < XXH_ACC_NB; i++) {
        XXH3_scalarRound(acc, input, secret, i);
    }
}
XXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(scalar)

/*!
 * @internal
 * @brief Scalar scramble step for @ref XXH3_scrambleAcc_scalar().
 *
 * This is extracted to its own function because the NEON path uses a combination
 * of NEON and scalar.
 */
XXH_FORCE_INLINE void
XXH3_scalarScrambleRound(void* XXH_RESTRICT acc,
                         void const* XXH_RESTRICT secret,
                         size_t lane)
{
    xxh_u64* const xacc = (xxh_u64*) acc;   /* presumed aligned */
    const xxh_u8* const xsecret = (const xxh_u8*) secret;   /* no alignment restriction */
    XXH_ASSERT((((size_t)acc) & (XXH_ACC_ALIGN-1)) == 0);
    XXH_ASSERT(lane < XXH_ACC_NB);
    {
        xxh_u64 const key64 = XXH_readLE64(xsecret + lane * 8);
        xxh_u64 acc64 = xacc[lane];
        acc64 = XXH_xorshift64(acc64, 47);
        acc64 ^= key64;
        acc64 *= XXH_PRIME32_1;
        xacc[lane] = acc64;
    }
}

/*!
 * @internal
 * @brief Scrambles the accumulators after a large chunk has been read
 */
XXH_FORCE_INLINE void
XXH3_scrambleAcc_scalar(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
{
    size_t i;
    for (i=0; i < XXH_ACC_NB; i++) {
        XXH3_scalarScrambleRound(acc, secret, i);
    }
}

XXH_FORCE_INLINE void
XXH3_initCustomSecret_scalar(void* XXH_RESTRICT customSecret, xxh_u64 seed64)
{
    /*
     * We need a separate pointer for the hack below,
     * which requires a non-const pointer.
     * Any decent compiler will optimize this out otherwise.
     */
    const xxh_u8* kSecretPtr = XXH3_kSecret;
    XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0);

#if defined(__GNUC__) && defined(__aarch64__)
    /*
     * UGLY HACK:
     * GCC and Clang generate a bunch of MOV/MOVK pairs for aarch64, and they are
     * placed sequentially, in order, at the top of the unrolled loop.
     *
     * While MOVK is great for generating constants (2 cycles for a 64-bit
     * constant compared to 4 cycles for LDR), it fights for bandwidth with
     * the arithmetic instructions.
     *
     *   I   L   S
     * MOVK
     * MOVK
     * MOVK
     * MOVK
     * ADD
     * SUB      STR
     *          STR
     * By forcing loads from memory (as the asm line causes the compiler to assume
     * that XXH3_kSecretPtr has been changed), the pipelines are used more
     * efficiently:
     *   I   L   S
     *      LDR
     *  ADD LDR
     *  SUB     STR
     *          STR
     *
     * See XXH3_NEON_LANES for details on the pipsline.
     *
     * XXH3_64bits_withSeed, len == 256, Snapdragon 835
     *   without hack: 2654.4 MB/s
     *   with hack:    3202.9 MB/s
     */
    XXH_COMPILER_GUARD(kSecretPtr);
#endif
    {   int const nbRounds = XXH_SECRET_DEFAULT_SIZE / 16;
        int i;
        for (i=0; i < nbRounds; i++) {
            /*
             * The asm hack causes the compiler to assume that kSecretPtr aliases with
             * customSecret, and on aarch64, this prevented LDP from merging two
             * loads together for free. Putting the loads together before the stores
             * properly generates LDP.
             */
            xxh_u64 lo = XXH_readLE64(kSecretPtr + 16*i)     + seed64;
            xxh_u64 hi = XXH_readLE64(kSecretPtr + 16*i + 8) - seed64;
            XXH_writeLE64((xxh_u8*)customSecret + 16*i,     lo);
            XXH_writeLE64((xxh_u8*)customSecret + 16*i + 8, hi);
    }   }
}


typedef void (*XXH3_f_accumulate)(xxh_u64* XXH_RESTRICT, const xxh_u8* XXH_RESTRICT, const xxh_u8* XXH_RESTRICT, size_t);
typedef void (*XXH3_f_scrambleAcc)(void* XXH_RESTRICT, const void*);
typedef void (*XXH3_f_initCustomSecret)(void* XXH_RESTRICT, xxh_u64);


#if (XXH_VECTOR == XXH_AVX512)

#define XXH3_accumulate_512 XXH3_accumulate_512_avx512
#define XXH3_accumulate     XXH3_accumulate_avx512
#define XXH3_scrambleAcc    XXH3_scrambleAcc_avx512
#define XXH3_initCustomSecret XXH3_initCustomSecret_avx512

#elif (XXH_VECTOR == XXH_AVX2)

#define XXH3_accumulate_512 XXH3_accumulate_512_avx2
#define XXH3_accumulate     XXH3_accumulate_avx2
#define XXH3_scrambleAcc    XXH3_scrambleAcc_avx2
#define XXH3_initCustomSecret XXH3_initCustomSecret_avx2

#elif (XXH_VECTOR == XXH_SSE2)

#define XXH3_accumulate_512 XXH3_accumulate_512_sse2
#define XXH3_accumulate     XXH3_accumulate_sse2
#define XXH3_scrambleAcc    XXH3_scrambleAcc_sse2
#define XXH3_initCustomSecret XXH3_initCustomSecret_sse2

#elif (XXH_VECTOR == XXH_NEON)

#define XXH3_accumulate_512 XXH3_accumulate_512_neon
#define XXH3_accumulate     XXH3_accumulate_neon
#define XXH3_scrambleAcc    XXH3_scrambleAcc_neon
#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar

#elif (XXH_VECTOR == XXH_VSX)

#define XXH3_accumulate_512 XXH3_accumulate_512_vsx
#define XXH3_accumulate     XXH3_accumulate_vsx
#define XXH3_scrambleAcc    XXH3_scrambleAcc_vsx
#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar

#elif (XXH_VECTOR == XXH_SVE)
#define XXH3_accumulate_512 XXH3_accumulate_512_sve
#define XXH3_accumulate     XXH3_accumulate_sve
#define XXH3_scrambleAcc    XXH3_scrambleAcc_scalar
#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar

#else /* scalar */

#define XXH3_accumulate_512 XXH3_accumulate_512_scalar
#define XXH3_accumulate     XXH3_accumulate_scalar
#define XXH3_scrambleAcc    XXH3_scrambleAcc_scalar
#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar

#endif

#if XXH_SIZE_OPT >= 1 /* don't do SIMD for initialization */
#  undef XXH3_initCustomSecret
#  define XXH3_initCustomSecret XXH3_initCustomSecret_scalar
#endif

XXH_FORCE_INLINE void
XXH3_hashLong_internal_loop(xxh_u64* XXH_RESTRICT acc,
                      const xxh_u8* XXH_RESTRICT input, size_t len,
                      const xxh_u8* XXH_RESTRICT secret, size_t secretSize,
                            XXH3_f_accumulate f_acc,
                            XXH3_f_scrambleAcc f_scramble)
{
    size_t const nbStripesPerBlock = (secretSize - XXH_STRIPE_LEN) / XXH_SECRET_CONSUME_RATE;
    size_t const block_len = XXH_STRIPE_LEN * nbStripesPerBlock;
    size_t const nb_blocks = (len - 1) / block_len;

    size_t n;

    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN);

    for (n = 0; n < nb_blocks; n++) {
        f_acc(acc, input + n*block_len, secret, nbStripesPerBlock);
        f_scramble(acc, secret + secretSize - XXH_STRIPE_LEN);
    }

    /* last partial block */
    XXH_ASSERT(len > XXH_STRIPE_LEN);
    {   size_t const nbStripes = ((len - 1) - (block_len * nb_blocks)) / XXH_STRIPE_LEN;
        XXH_ASSERT(nbStripes <= (secretSize / XXH_SECRET_CONSUME_RATE));
        f_acc(acc, input + nb_blocks*block_len, secret, nbStripes);

        /* last stripe */
        {   const xxh_u8* const p = input + len - XXH_STRIPE_LEN;
#define XXH_SECRET_LASTACC_START 7  /* not aligned on 8, last secret is different from acc & scrambler */
            XXH3_accumulate_512(acc, p, secret + secretSize - XXH_STRIPE_LEN - XXH_SECRET_LASTACC_START);
    }   }
}

XXH_FORCE_INLINE xxh_u64
XXH3_mix2Accs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret)
{
    return XXH3_mul128_fold64(
               acc[0] ^ XXH_readLE64(secret),
               acc[1] ^ XXH_readLE64(secret+8) );
}

static XXH64_hash_t
XXH3_mergeAccs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret, xxh_u64 start)
{
    xxh_u64 result64 = start;
    size_t i = 0;

    for (i = 0; i < 4; i++) {
        result64 += XXH3_mix2Accs(acc+2*i, secret + 16*i);
#if defined(__clang__)                                /* Clang */ \
    && (defined(__arm__) || defined(__thumb__))       /* ARMv7 */ \
    && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */  \
    && !defined(XXH_ENABLE_AUTOVECTORIZE)             /* Define to disable */
        /*
         * UGLY HACK:
         * Prevent autovectorization on Clang ARMv7-a. Exact same problem as
         * the one in XXH3_len_129to240_64b. Speeds up shorter keys > 240b.
         * XXH3_64bits, len == 256, Snapdragon 835:
         *   without hack: 2063.7 MB/s
         *   with hack:    2560.7 MB/s
         */
        XXH_COMPILER_GUARD(result64);
#endif
    }

    return XXH3_avalanche(result64);
}

#define XXH3_INIT_ACC { XXH_PRIME32_3, XXH_PRIME64_1, XXH_PRIME64_2, XXH_PRIME64_3, \
                        XXH_PRIME64_4, XXH_PRIME32_2, XXH_PRIME64_5, XXH_PRIME32_1 }

XXH_FORCE_INLINE XXH64_hash_t
XXH3_hashLong_64b_internal(const void* XXH_RESTRICT input, size_t len,
                           const void* XXH_RESTRICT secret, size_t secretSize,
                           XXH3_f_accumulate f_acc,
                           XXH3_f_scrambleAcc f_scramble)
{
    XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC;

    XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, (const xxh_u8*)secret, secretSize, f_acc, f_scramble);

    /* converge into final hash */
    XXH_STATIC_ASSERT(sizeof(acc) == 64);
    /* do not align on 8, so that the secret is different from the accumulator */
#define XXH_SECRET_MERGEACCS_START 11
    XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START);
    return XXH3_mergeAccs(acc, (const xxh_u8*)secret + XXH_SECRET_MERGEACCS_START, (xxh_u64)len * XXH_PRIME64_1);
}

/*
 * It's important for performance to transmit secret's size (when it's static)
 * so that the compiler can properly optimize the vectorized loop.
 * This makes a big performance difference for "medium" keys (<1 KB) when using AVX instruction set.
 * When the secret size is unknown, or on GCC 12 where the mix of NO_INLINE and FORCE_INLINE
 * breaks -Og, this is XXH_NO_INLINE.
 */
XXH3_WITH_SECRET_INLINE XXH64_hash_t
XXH3_hashLong_64b_withSecret(const void* XXH_RESTRICT input, size_t len,
                             XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen)
{
    (void)seed64;
    return XXH3_hashLong_64b_internal(input, len, secret, secretLen, XXH3_accumulate, XXH3_scrambleAcc);
}

/*
 * It's preferable for performance that XXH3_hashLong is not inlined,
 * as it results in a smaller function for small data, easier to the instruction cache.
 * Note that inside this no_inline function, we do inline the internal loop,
 * and provide a statically defined secret size to allow optimization of vector loop.
 */
XXH_NO_INLINE XXH_PUREF XXH64_hash_t
XXH3_hashLong_64b_default(const void* XXH_RESTRICT input, size_t len,
                          XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen)
{
    (void)seed64; (void)secret; (void)secretLen;
    return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate, XXH3_scrambleAcc);
}

/*
 * XXH3_hashLong_64b_withSeed():
 * Generate a custom key based on alteration of default XXH3_kSecret with the seed,
 * and then use this key for long mode hashing.
 *
 * This operation is decently fast but nonetheless costs a little bit of time.
 * Try to avoid it whenever possible (typically when seed==0).
 *
 * It's important for performance that XXH3_hashLong is not inlined. Not sure
 * why (uop cache maybe?), but the difference is large and easily measurable.
 */
XXH_FORCE_INLINE XXH64_hash_t
XXH3_hashLong_64b_withSeed_internal(const void* input, size_t len,
                                    XXH64_hash_t seed,
                                    XXH3_f_accumulate f_acc,
                                    XXH3_f_scrambleAcc f_scramble,
                                    XXH3_f_initCustomSecret f_initSec)
{
#if XXH_SIZE_OPT <= 0
    if (seed == 0)
        return XXH3_hashLong_64b_internal(input, len,
                                          XXH3_kSecret, sizeof(XXH3_kSecret),
                                          f_acc, f_scramble);
#endif
    {   XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE];
        f_initSec(secret, seed);
        return XXH3_hashLong_64b_internal(input, len, secret, sizeof(secret),
                                          f_acc, f_scramble);
    }
}

/*
 * It's important for performance that XXH3_hashLong is not inlined.
 */
XXH_NO_INLINE XXH64_hash_t
XXH3_hashLong_64b_withSeed(const void* XXH_RESTRICT input, size_t len,
                           XXH64_hash_t seed, const xxh_u8* XXH_RESTRICT secret, size_t secretLen)
{
    (void)secret; (void)secretLen;
    return XXH3_hashLong_64b_withSeed_internal(input, len, seed,
                XXH3_accumulate, XXH3_scrambleAcc, XXH3_initCustomSecret);
}


typedef XXH64_hash_t (*XXH3_hashLong64_f)(const void* XXH_RESTRICT, size_t,
                                          XXH64_hash_t, const xxh_u8* XXH_RESTRICT, size_t);

XXH_FORCE_INLINE XXH64_hash_t
XXH3_64bits_internal(const void* XXH_RESTRICT input, size_t len,
                     XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen,
                     XXH3_hashLong64_f f_hashLong)
{
    XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN);
    /*
     * If an action is to be taken if `secretLen` condition is not respected,
     * it should be done here.
     * For now, it's a contract pre-condition.
     * Adding a check and a branch here would cost performance at every hash.
     * Also, note that function signature doesn't offer room to return an error.
     */
    if (len <= 16)
        return XXH3_len_0to16_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, seed64);
    if (len <= 128)
        return XXH3_len_17to128_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64);
    if (len <= XXH3_MIDSIZE_MAX)
        return XXH3_len_129to240_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64);
    return f_hashLong(input, len, seed64, (const xxh_u8*)secret, secretLen);
}


/* ===   Public entry point   === */

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(XXH_NOESCAPE const void* input, size_t length)
{
    return XXH3_64bits_internal(input, length, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_default);
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH64_hash_t
XXH3_64bits_withSecret(XXH_NOESCAPE const void* input, size_t length, XXH_NOESCAPE const void* secret, size_t secretSize)
{
    return XXH3_64bits_internal(input, length, 0, secret, secretSize, XXH3_hashLong_64b_withSecret);
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH64_hash_t
XXH3_64bits_withSeed(XXH_NOESCAPE const void* input, size_t length, XXH64_hash_t seed)
{
    return XXH3_64bits_internal(input, length, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed);
}

XXH_PUBLIC_API XXH64_hash_t
XXH3_64bits_withSecretandSeed(XXH_NOESCAPE const void* input, size_t length, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed)
{
    if (length <= XXH3_MIDSIZE_MAX)
        return XXH3_64bits_internal(input, length, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL);
    return XXH3_hashLong_64b_withSecret(input, length, seed, (const xxh_u8*)secret, secretSize);
}


/* ===   XXH3 streaming   === */
#ifndef XXH_NO_STREAM
/*
 * Malloc's a pointer that is always aligned to align.
 *
 * This must be freed with `XXH_alignedFree()`.
 *
 * malloc typically guarantees 16 byte alignment on 64-bit systems and 8 byte
 * alignment on 32-bit. This isn't enough for the 32 byte aligned loads in AVX2
 * or on 32-bit, the 16 byte aligned loads in SSE2 and NEON.
 *
 * This underalignment previously caused a rather obvious crash which went
 * completely unnoticed due to XXH3_createState() not actually being tested.
 * Credit to RedSpah for noticing this bug.
 *
 * The alignment is done manually: Functions like posix_memalign or _mm_malloc
 * are avoided: To maintain portability, we would have to write a fallback
 * like this anyways, and besides, testing for the existence of library
 * functions without relying on external build tools is impossible.
 *
 * The method is simple: Overallocate, manually align, and store the offset
 * to the original behind the returned pointer.
 *
 * Align must be a power of 2 and 8 <= align <= 128.
 */
static XXH_MALLOCF void* XXH_alignedMalloc(size_t s, size_t align)
{
    XXH_ASSERT(align <= 128 && align >= 8); /* range check */
    XXH_ASSERT((align & (align-1)) == 0);   /* power of 2 */
    XXH_ASSERT(s != 0 && s < (s + align));  /* empty/overflow */
    {   /* Overallocate to make room for manual realignment and an offset byte */
        xxh_u8* base = (xxh_u8*)XXH_malloc(s + align);
        if (base != NULL) {
            /*
             * Get the offset needed to align this pointer.
             *
             * Even if the returned pointer is aligned, there will always be
             * at least one byte to store the offset to the original pointer.
             */
            size_t offset = align - ((size_t)base & (align - 1)); /* base % align */
            /* Add the offset for the now-aligned pointer */
            xxh_u8* ptr = base + offset;

            XXH_ASSERT((size_t)ptr % align == 0);

            /* Store the offset immediately before the returned pointer. */
            ptr[-1] = (xxh_u8)offset;
            return ptr;
        }
        return NULL;
    }
}
/*
 * Frees an aligned pointer allocated by XXH_alignedMalloc(). Don't pass
 * normal malloc'd pointers, XXH_alignedMalloc has a specific data layout.
 */
static void XXH_alignedFree(void* p)
{
    if (p != NULL) {
        xxh_u8* ptr = (xxh_u8*)p;
        /* Get the offset byte we added in XXH_malloc. */
        xxh_u8 offset = ptr[-1];
        /* Free the original malloc'd pointer */
        xxh_u8* base = ptr - offset;
        XXH_free(base);
    }
}
/*! @ingroup XXH3_family */
/*!
 * @brief Allocate an @ref XXH3_state_t.
 *
 * @return An allocated pointer of @ref XXH3_state_t on success.
 * @return `NULL` on failure.
 *
 * @note Must be freed with XXH3_freeState().
 *
 * @see @ref streaming_example "Streaming Example"
 */
XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void)
{
    XXH3_state_t* const state = (XXH3_state_t*)XXH_alignedMalloc(sizeof(XXH3_state_t), 64);
    if (state==NULL) return NULL;
    XXH3_INITSTATE(state);
    return state;
}

/*! @ingroup XXH3_family */
/*!
 * @brief Frees an @ref XXH3_state_t.
 *
 * @param statePtr A pointer to an @ref XXH3_state_t allocated with @ref XXH3_createState().
 *
 * @return @ref XXH_OK.
 *
 * @note Must be allocated with XXH3_createState().
 *
 * @see @ref streaming_example "Streaming Example"
 */
XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr)
{
    XXH_alignedFree(statePtr);
    return XXH_OK;
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API void
XXH3_copyState(XXH_NOESCAPE XXH3_state_t* dst_state, XXH_NOESCAPE const XXH3_state_t* src_state)
{
    XXH_memcpy(dst_state, src_state, sizeof(*dst_state));
}

static void
XXH3_reset_internal(XXH3_state_t* statePtr,
                    XXH64_hash_t seed,
                    const void* secret, size_t secretSize)
{
    size_t const initStart = offsetof(XXH3_state_t, bufferedSize);
    size_t const initLength = offsetof(XXH3_state_t, nbStripesPerBlock) - initStart;
    XXH_ASSERT(offsetof(XXH3_state_t, nbStripesPerBlock) > initStart);
    XXH_ASSERT(statePtr != NULL);
    /* set members from bufferedSize to nbStripesPerBlock (excluded) to 0 */
    memset((char*)statePtr + initStart, 0, initLength);
    statePtr->acc[0] = XXH_PRIME32_3;
    statePtr->acc[1] = XXH_PRIME64_1;
    statePtr->acc[2] = XXH_PRIME64_2;
    statePtr->acc[3] = XXH_PRIME64_3;
    statePtr->acc[4] = XXH_PRIME64_4;
    statePtr->acc[5] = XXH_PRIME32_2;
    statePtr->acc[6] = XXH_PRIME64_5;
    statePtr->acc[7] = XXH_PRIME32_1;
    statePtr->seed = seed;
    statePtr->useSeed = (seed != 0);
    statePtr->extSecret = (const unsigned char*)secret;
    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN);
    statePtr->secretLimit = secretSize - XXH_STRIPE_LEN;
    statePtr->nbStripesPerBlock = statePtr->secretLimit / XXH_SECRET_CONSUME_RATE;
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH_errorcode
XXH3_64bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr)
{
    if (statePtr == NULL) return XXH_ERROR;
    XXH3_reset_internal(statePtr, 0, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE);
    return XXH_OK;
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH_errorcode
XXH3_64bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize)
{
    if (statePtr == NULL) return XXH_ERROR;
    XXH3_reset_internal(statePtr, 0, secret, secretSize);
    if (secret == NULL) return XXH_ERROR;
    if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR;
    return XXH_OK;
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH_errorcode
XXH3_64bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed)
{
    if (statePtr == NULL) return XXH_ERROR;
    if (seed==0) return XXH3_64bits_reset(statePtr);
    if ((seed != statePtr->seed) || (statePtr->extSecret != NULL))
        XXH3_initCustomSecret(statePtr->customSecret, seed);
    XXH3_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE);
    return XXH_OK;
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH_errorcode
XXH3_64bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed64)
{
    if (statePtr == NULL) return XXH_ERROR;
    if (secret == NULL) return XXH_ERROR;
    if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR;
    XXH3_reset_internal(statePtr, seed64, secret, secretSize);
    statePtr->useSeed = 1; /* always, even if seed64==0 */
    return XXH_OK;
}

/*!
 * @internal
 * @brief Processes a large input for XXH3_update() and XXH3_digest_long().
 *
 * Unlike XXH3_hashLong_internal_loop(), this can process data that overlaps a block.
 *
 * @param acc                Pointer to the 8 accumulator lanes
 * @param nbStripesSoFarPtr  In/out pointer to the number of leftover stripes in the block*
 * @param nbStripesPerBlock  Number of stripes in a block
 * @param input              Input pointer
 * @param nbStripes          Number of stripes to process
 * @param secret             Secret pointer
 * @param secretLimit        Offset of the last block in @p secret
 * @param f_acc              Pointer to an XXH3_accumulate implementation
 * @param f_scramble         Pointer to an XXH3_scrambleAcc implementation
 * @return                   Pointer past the end of @p input after processing
 */
XXH_FORCE_INLINE const xxh_u8 *
XXH3_consumeStripes(xxh_u64* XXH_RESTRICT acc,
                    size_t* XXH_RESTRICT nbStripesSoFarPtr, size_t nbStripesPerBlock,
                    const xxh_u8* XXH_RESTRICT input, size_t nbStripes,
                    const xxh_u8* XXH_RESTRICT secret, size_t secretLimit,
                    XXH3_f_accumulate f_acc,
                    XXH3_f_scrambleAcc f_scramble)
{
    const xxh_u8* initialSecret = secret + *nbStripesSoFarPtr * XXH_SECRET_CONSUME_RATE;
    /* Process full blocks */
    if (nbStripes >= (nbStripesPerBlock - *nbStripesSoFarPtr)) {
        /* Process the initial partial block... */
        size_t nbStripesThisIter = nbStripesPerBlock - *nbStripesSoFarPtr;

        do {
            /* Accumulate and scramble */
            f_acc(acc, input, initialSecret, nbStripesThisIter);
            f_scramble(acc, secret + secretLimit);
            input += nbStripesThisIter * XXH_STRIPE_LEN;
            nbStripes -= nbStripesThisIter;
            /* Then continue the loop with the full block size */
            nbStripesThisIter = nbStripesPerBlock;
            initialSecret = secret;
        } while (nbStripes >= nbStripesPerBlock);
        *nbStripesSoFarPtr = 0;
    }
    /* Process a partial block */
    if (nbStripes > 0) {
        f_acc(acc, input, initialSecret, nbStripes);
        input += nbStripes * XXH_STRIPE_LEN;
        *nbStripesSoFarPtr += nbStripes;
    }
    /* Return end pointer */
    return input;
}

#ifndef XXH3_STREAM_USE_STACK
# if XXH_SIZE_OPT <= 0 && !defined(__clang__) /* clang doesn't need additional stack space */
#   define XXH3_STREAM_USE_STACK 1
# endif
#endif
/*
 * Both XXH3_64bits_update and XXH3_128bits_update use this routine.
 */
XXH_FORCE_INLINE XXH_errorcode
XXH3_update(XXH3_state_t* XXH_RESTRICT const state,
            const xxh_u8* XXH_RESTRICT input, size_t len,
            XXH3_f_accumulate f_acc,
            XXH3_f_scrambleAcc f_scramble)
{
    if (input==NULL) {
        XXH_ASSERT(len == 0);
        return XXH_OK;
    }

    XXH_ASSERT(state != NULL);
    {   const xxh_u8* const bEnd = input + len;
        const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret;
#if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1
        /* For some reason, gcc and MSVC seem to suffer greatly
         * when operating accumulators directly into state.
         * Operating into stack space seems to enable proper optimization.
         * clang, on the other hand, doesn't seem to need this trick */
        XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[8];
        XXH_memcpy(acc, state->acc, sizeof(acc));
#else
        xxh_u64* XXH_RESTRICT const acc = state->acc;
#endif
        state->totalLen += len;
        XXH_ASSERT(state->bufferedSize <= XXH3_INTERNALBUFFER_SIZE);

        /* small input : just fill in tmp buffer */
        if (len <= XXH3_INTERNALBUFFER_SIZE - state->bufferedSize) {
            XXH_memcpy(state->buffer + state->bufferedSize, input, len);
            state->bufferedSize += (XXH32_hash_t)len;
            return XXH_OK;
        }

        /* total input is now > XXH3_INTERNALBUFFER_SIZE */
        #define XXH3_INTERNALBUFFER_STRIPES (XXH3_INTERNALBUFFER_SIZE / XXH_STRIPE_LEN)
        XXH_STATIC_ASSERT(XXH3_INTERNALBUFFER_SIZE % XXH_STRIPE_LEN == 0);   /* clean multiple */

        /*
         * Internal buffer is partially filled (always, except at beginning)
         * Complete it, then consume it.
         */
        if (state->bufferedSize) {
            size_t const loadSize = XXH3_INTERNALBUFFER_SIZE - state->bufferedSize;
            XXH_memcpy(state->buffer + state->bufferedSize, input, loadSize);
            input += loadSize;
            XXH3_consumeStripes(acc,
                               &state->nbStripesSoFar, state->nbStripesPerBlock,
                                state->buffer, XXH3_INTERNALBUFFER_STRIPES,
                                secret, state->secretLimit,
                                f_acc, f_scramble);
            state->bufferedSize = 0;
        }
        XXH_ASSERT(input < bEnd);
        if (bEnd - input > XXH3_INTERNALBUFFER_SIZE) {
            size_t nbStripes = (size_t)(bEnd - 1 - input) / XXH_STRIPE_LEN;
            input = XXH3_consumeStripes(acc,
                                       &state->nbStripesSoFar, state->nbStripesPerBlock,
                                       input, nbStripes,
                                       secret, state->secretLimit,
                                       f_acc, f_scramble);
            XXH_memcpy(state->buffer + sizeof(state->buffer) - XXH_STRIPE_LEN, input - XXH_STRIPE_LEN, XXH_STRIPE_LEN);

        }
        /* Some remaining input (always) : buffer it */
        XXH_ASSERT(input < bEnd);
        XXH_ASSERT(bEnd - input <= XXH3_INTERNALBUFFER_SIZE);
        XXH_ASSERT(state->bufferedSize == 0);
        XXH_memcpy(state->buffer, input, (size_t)(bEnd-input));
        state->bufferedSize = (XXH32_hash_t)(bEnd-input);
#if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1
        /* save stack accumulators into state */
        XXH_memcpy(state->acc, acc, sizeof(acc));
#endif
    }

    return XXH_OK;
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH_errorcode
XXH3_64bits_update(XXH_NOESCAPE XXH3_state_t* state, XXH_NOESCAPE const void* input, size_t len)
{
    return XXH3_update(state, (const xxh_u8*)input, len,
                       XXH3_accumulate, XXH3_scrambleAcc);
}


XXH_FORCE_INLINE void
XXH3_digest_long (XXH64_hash_t* acc,
                  const XXH3_state_t* state,
                  const unsigned char* secret)
{
    xxh_u8 lastStripe[XXH_STRIPE_LEN];
    const xxh_u8* lastStripePtr;

    /*
     * Digest on a local copy. This way, the state remains unaltered, and it can
     * continue ingesting more input afterwards.
     */
    XXH_memcpy(acc, state->acc, sizeof(state->acc));
    if (state->bufferedSize >= XXH_STRIPE_LEN) {
        /* Consume remaining stripes then point to remaining data in buffer */
        size_t const nbStripes = (state->bufferedSize - 1) / XXH_STRIPE_LEN;
        size_t nbStripesSoFar = state->nbStripesSoFar;
        XXH3_consumeStripes(acc,
                           &nbStripesSoFar, state->nbStripesPerBlock,
                            state->buffer, nbStripes,
                            secret, state->secretLimit,
                            XXH3_accumulate, XXH3_scrambleAcc);
        lastStripePtr = state->buffer + state->bufferedSize - XXH_STRIPE_LEN;
    } else {  /* bufferedSize < XXH_STRIPE_LEN */
        /* Copy to temp buffer */
        size_t const catchupSize = XXH_STRIPE_LEN - state->bufferedSize;
        XXH_ASSERT(state->bufferedSize > 0);  /* there is always some input buffered */
        XXH_memcpy(lastStripe, state->buffer + sizeof(state->buffer) - catchupSize, catchupSize);
        XXH_memcpy(lastStripe + catchupSize, state->buffer, state->bufferedSize);
        lastStripePtr = lastStripe;
    }
    /* Last stripe */
    XXH3_accumulate_512(acc,
                        lastStripePtr,
                        secret + state->secretLimit - XXH_SECRET_LASTACC_START);
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (XXH_NOESCAPE const XXH3_state_t* state)
{
    const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret;
    if (state->totalLen > XXH3_MIDSIZE_MAX) {
        XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB];
        XXH3_digest_long(acc, state, secret);
        return XXH3_mergeAccs(acc,
                              secret + XXH_SECRET_MERGEACCS_START,
                              (xxh_u64)state->totalLen * XXH_PRIME64_1);
    }
    /* totalLen <= XXH3_MIDSIZE_MAX: digesting a short input */
    if (state->useSeed)
        return XXH3_64bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed);
    return XXH3_64bits_withSecret(state->buffer, (size_t)(state->totalLen),
                                  secret, state->secretLimit + XXH_STRIPE_LEN);
}
#endif /* !XXH_NO_STREAM */


/* ==========================================
 * XXH3 128 bits (a.k.a XXH128)
 * ==========================================
 * XXH3's 128-bit variant has better mixing and strength than the 64-bit variant,
 * even without counting the significantly larger output size.
 *
 * For example, extra steps are taken to avoid the seed-dependent collisions
 * in 17-240 byte inputs (See XXH3_mix16B and XXH128_mix32B).
 *
 * This strength naturally comes at the cost of some speed, especially on short
 * lengths. Note that longer hashes are about as fast as the 64-bit version
 * due to it using only a slight modification of the 64-bit loop.
 *
 * XXH128 is also more oriented towards 64-bit machines. It is still extremely
 * fast for a _128-bit_ hash on 32-bit (it usually clears XXH64).
 */

XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t
XXH3_len_1to3_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
{
    /* A doubled version of 1to3_64b with different constants. */
    XXH_ASSERT(input != NULL);
    XXH_ASSERT(1 <= len && len <= 3);
    XXH_ASSERT(secret != NULL);
    /*
     * len = 1: combinedl = { input[0], 0x01, input[0], input[0] }
     * len = 2: combinedl = { input[1], 0x02, input[0], input[1] }
     * len = 3: combinedl = { input[2], 0x03, input[0], input[1] }
     */
    {   xxh_u8 const c1 = input[0];
        xxh_u8 const c2 = input[len >> 1];
        xxh_u8 const c3 = input[len - 1];
        xxh_u32 const combinedl = ((xxh_u32)c1 <<16) | ((xxh_u32)c2 << 24)
                                | ((xxh_u32)c3 << 0) | ((xxh_u32)len << 8);
        xxh_u32 const combinedh = XXH_rotl32(XXH_swap32(combinedl), 13);
        xxh_u64 const bitflipl = (XXH_readLE32(secret) ^ XXH_readLE32(secret+4)) + seed;
        xxh_u64 const bitfliph = (XXH_readLE32(secret+8) ^ XXH_readLE32(secret+12)) - seed;
        xxh_u64 const keyed_lo = (xxh_u64)combinedl ^ bitflipl;
        xxh_u64 const keyed_hi = (xxh_u64)combinedh ^ bitfliph;
        XXH128_hash_t h128;
        h128.low64  = XXH64_avalanche(keyed_lo);
        h128.high64 = XXH64_avalanche(keyed_hi);
        return h128;
    }
}

XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t
XXH3_len_4to8_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
{
    XXH_ASSERT(input != NULL);
    XXH_ASSERT(secret != NULL);
    XXH_ASSERT(4 <= len && len <= 8);
    seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32;
    {   xxh_u32 const input_lo = XXH_readLE32(input);
        xxh_u32 const input_hi = XXH_readLE32(input + len - 4);
        xxh_u64 const input_64 = input_lo + ((xxh_u64)input_hi << 32);
        xxh_u64 const bitflip = (XXH_readLE64(secret+16) ^ XXH_readLE64(secret+24)) + seed;
        xxh_u64 const keyed = input_64 ^ bitflip;

        /* Shift len to the left to ensure it is even, this avoids even multiplies. */
        XXH128_hash_t m128 = XXH_mult64to128(keyed, XXH_PRIME64_1 + (len << 2));

        m128.high64 += (m128.low64 << 1);
        m128.low64  ^= (m128.high64 >> 3);

        m128.low64   = XXH_xorshift64(m128.low64, 35);
        m128.low64  *= PRIME_MX2;
        m128.low64   = XXH_xorshift64(m128.low64, 28);
        m128.high64  = XXH3_avalanche(m128.high64);
        return m128;
    }
}

XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t
XXH3_len_9to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
{
    XXH_ASSERT(input != NULL);
    XXH_ASSERT(secret != NULL);
    XXH_ASSERT(9 <= len && len <= 16);
    {   xxh_u64 const bitflipl = (XXH_readLE64(secret+32) ^ XXH_readLE64(secret+40)) - seed;
        xxh_u64 const bitfliph = (XXH_readLE64(secret+48) ^ XXH_readLE64(secret+56)) + seed;
        xxh_u64 const input_lo = XXH_readLE64(input);
        xxh_u64       input_hi = XXH_readLE64(input + len - 8);
        XXH128_hash_t m128 = XXH_mult64to128(input_lo ^ input_hi ^ bitflipl, XXH_PRIME64_1);
        /*
         * Put len in the middle of m128 to ensure that the length gets mixed to
         * both the low and high bits in the 128x64 multiply below.
         */
        m128.low64 += (xxh_u64)(len - 1) << 54;
        input_hi   ^= bitfliph;
        /*
         * Add the high 32 bits of input_hi to the high 32 bits of m128, then
         * add the long product of the low 32 bits of input_hi and XXH_PRIME32_2 to
         * the high 64 bits of m128.
         *
         * The best approach to this operation is different on 32-bit and 64-bit.
         */
        if (sizeof(void *) < sizeof(xxh_u64)) { /* 32-bit */
            /*
             * 32-bit optimized version, which is more readable.
             *
             * On 32-bit, it removes an ADC and delays a dependency between the two
             * halves of m128.high64, but it generates an extra mask on 64-bit.
             */
            m128.high64 += (input_hi & 0xFFFFFFFF00000000ULL) + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2);
        } else {
            /*
             * 64-bit optimized (albeit more confusing) version.
             *
             * Uses some properties of addition and multiplication to remove the mask:
             *
             * Let:
             *    a = input_hi.lo = (input_hi & 0x00000000FFFFFFFF)
             *    b = input_hi.hi = (input_hi & 0xFFFFFFFF00000000)
             *    c = XXH_PRIME32_2
             *
             *    a + (b * c)
             * Inverse Property: x + y - x == y
             *    a + (b * (1 + c - 1))
             * Distributive Property: x * (y + z) == (x * y) + (x * z)
             *    a + (b * 1) + (b * (c - 1))
             * Identity Property: x * 1 == x
             *    a + b + (b * (c - 1))
             *
             * Substitute a, b, and c:
             *    input_hi.hi + input_hi.lo + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1))
             *
             * Since input_hi.hi + input_hi.lo == input_hi, we get this:
             *    input_hi + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1))
             */
            m128.high64 += input_hi + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2 - 1);
        }
        /* m128 ^= XXH_swap64(m128 >> 64); */
        m128.low64  ^= XXH_swap64(m128.high64);

        {   /* 128x64 multiply: h128 = m128 * XXH_PRIME64_2; */
            XXH128_hash_t h128 = XXH_mult64to128(m128.low64, XXH_PRIME64_2);
            h128.high64 += m128.high64 * XXH_PRIME64_2;

            h128.low64   = XXH3_avalanche(h128.low64);
            h128.high64  = XXH3_avalanche(h128.high64);
            return h128;
    }   }
}

/*
 * Assumption: `secret` size is >= XXH3_SECRET_SIZE_MIN
 */
XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t
XXH3_len_0to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
{
    XXH_ASSERT(len <= 16);
    {   if (len > 8) return XXH3_len_9to16_128b(input, len, secret, seed);
        if (len >= 4) return XXH3_len_4to8_128b(input, len, secret, seed);
        if (len) return XXH3_len_1to3_128b(input, len, secret, seed);
        {   XXH128_hash_t h128;
            xxh_u64 const bitflipl = XXH_readLE64(secret+64) ^ XXH_readLE64(secret+72);
            xxh_u64 const bitfliph = XXH_readLE64(secret+80) ^ XXH_readLE64(secret+88);
            h128.low64 = XXH64_avalanche(seed ^ bitflipl);
            h128.high64 = XXH64_avalanche( seed ^ bitfliph);
            return h128;
    }   }
}

/*
 * A bit slower than XXH3_mix16B, but handles multiply by zero better.
 */
XXH_FORCE_INLINE XXH128_hash_t
XXH128_mix32B(XXH128_hash_t acc, const xxh_u8* input_1, const xxh_u8* input_2,
              const xxh_u8* secret, XXH64_hash_t seed)
{
    acc.low64  += XXH3_mix16B (input_1, secret+0, seed);
    acc.low64  ^= XXH_readLE64(input_2) + XXH_readLE64(input_2 + 8);
    acc.high64 += XXH3_mix16B (input_2, secret+16, seed);
    acc.high64 ^= XXH_readLE64(input_1) + XXH_readLE64(input_1 + 8);
    return acc;
}


XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t
XXH3_len_17to128_128b(const xxh_u8* XXH_RESTRICT input, size_t len,
                      const xxh_u8* XXH_RESTRICT secret, size_t secretSize,
                      XXH64_hash_t seed)
{
    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;
    XXH_ASSERT(16 < len && len <= 128);

    {   XXH128_hash_t acc;
        acc.low64 = len * XXH_PRIME64_1;
        acc.high64 = 0;

#if XXH_SIZE_OPT >= 1
        {
            /* Smaller, but slightly slower. */
            unsigned int i = (unsigned int)(len - 1) / 32;
            do {
                acc = XXH128_mix32B(acc, input+16*i, input+len-16*(i+1), secret+32*i, seed);
            } while (i-- != 0);
        }
#else
        if (len > 32) {
            if (len > 64) {
                if (len > 96) {
                    acc = XXH128_mix32B(acc, input+48, input+len-64, secret+96, seed);
                }
                acc = XXH128_mix32B(acc, input+32, input+len-48, secret+64, seed);
            }
            acc = XXH128_mix32B(acc, input+16, input+len-32, secret+32, seed);
        }
        acc = XXH128_mix32B(acc, input, input+len-16, secret, seed);
#endif
        {   XXH128_hash_t h128;
            h128.low64  = acc.low64 + acc.high64;
            h128.high64 = (acc.low64    * XXH_PRIME64_1)
                        + (acc.high64   * XXH_PRIME64_4)
                        + ((len - seed) * XXH_PRIME64_2);
            h128.low64  = XXH3_avalanche(h128.low64);
            h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64);
            return h128;
        }
    }
}

XXH_NO_INLINE XXH_PUREF XXH128_hash_t
XXH3_len_129to240_128b(const xxh_u8* XXH_RESTRICT input, size_t len,
                       const xxh_u8* XXH_RESTRICT secret, size_t secretSize,
                       XXH64_hash_t seed)
{
    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;
    XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX);

    {   XXH128_hash_t acc;
        unsigned i;
        acc.low64 = len * XXH_PRIME64_1;
        acc.high64 = 0;
        /*
         *  We set as `i` as offset + 32. We do this so that unchanged
         * `len` can be used as upper bound. This reaches a sweet spot
         * where both x86 and aarch64 get simple agen and good codegen
         * for the loop.
         */
        for (i = 32; i < 160; i += 32) {
            acc = XXH128_mix32B(acc,
                                input  + i - 32,
                                input  + i - 16,
                                secret + i - 32,
                                seed);
        }
        acc.low64 = XXH3_avalanche(acc.low64);
        acc.high64 = XXH3_avalanche(acc.high64);
        /*
         * NB: `i <= len` will duplicate the last 32-bytes if
         * len % 32 was zero. This is an unfortunate necessity to keep
         * the hash result stable.
         */
        for (i=160; i <= len; i += 32) {
            acc = XXH128_mix32B(acc,
                                input + i - 32,
                                input + i - 16,
                                secret + XXH3_MIDSIZE_STARTOFFSET + i - 160,
                                seed);
        }
        /* last bytes */
        acc = XXH128_mix32B(acc,
                            input + len - 16,
                            input + len - 32,
                            secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET - 16,
                            (XXH64_hash_t)0 - seed);

        {   XXH128_hash_t h128;
            h128.low64  = acc.low64 + acc.high64;
            h128.high64 = (acc.low64    * XXH_PRIME64_1)
                        + (acc.high64   * XXH_PRIME64_4)
                        + ((len - seed) * XXH_PRIME64_2);
            h128.low64  = XXH3_avalanche(h128.low64);
            h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64);
            return h128;
        }
    }
}

XXH_FORCE_INLINE XXH128_hash_t
XXH3_hashLong_128b_internal(const void* XXH_RESTRICT input, size_t len,
                            const xxh_u8* XXH_RESTRICT secret, size_t secretSize,
                            XXH3_f_accumulate f_acc,
                            XXH3_f_scrambleAcc f_scramble)
{
    XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC;

    XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, secret, secretSize, f_acc, f_scramble);

    /* converge into final hash */
    XXH_STATIC_ASSERT(sizeof(acc) == 64);
    XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START);
    {   XXH128_hash_t h128;
        h128.low64  = XXH3_mergeAccs(acc,
                                     secret + XXH_SECRET_MERGEACCS_START,
                                     (xxh_u64)len * XXH_PRIME64_1);
        h128.high64 = XXH3_mergeAccs(acc,
                                     secret + secretSize
                                            - sizeof(acc) - XXH_SECRET_MERGEACCS_START,
                                     ~((xxh_u64)len * XXH_PRIME64_2));
        return h128;
    }
}

/*
 * It's important for performance that XXH3_hashLong() is not inlined.
 */
XXH_NO_INLINE XXH_PUREF XXH128_hash_t
XXH3_hashLong_128b_default(const void* XXH_RESTRICT input, size_t len,
                           XXH64_hash_t seed64,
                           const void* XXH_RESTRICT secret, size_t secretLen)
{
    (void)seed64; (void)secret; (void)secretLen;
    return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret),
                                       XXH3_accumulate, XXH3_scrambleAcc);
}

/*
 * It's important for performance to pass @p secretLen (when it's static)
 * to the compiler, so that it can properly optimize the vectorized loop.
 *
 * When the secret size is unknown, or on GCC 12 where the mix of NO_INLINE and FORCE_INLINE
 * breaks -Og, this is XXH_NO_INLINE.
 */
XXH3_WITH_SECRET_INLINE XXH128_hash_t
XXH3_hashLong_128b_withSecret(const void* XXH_RESTRICT input, size_t len,
                              XXH64_hash_t seed64,
                              const void* XXH_RESTRICT secret, size_t secretLen)
{
    (void)seed64;
    return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, secretLen,
                                       XXH3_accumulate, XXH3_scrambleAcc);
}

XXH_FORCE_INLINE XXH128_hash_t
XXH3_hashLong_128b_withSeed_internal(const void* XXH_RESTRICT input, size_t len,
                                XXH64_hash_t seed64,
                                XXH3_f_accumulate f_acc,
                                XXH3_f_scrambleAcc f_scramble,
                                XXH3_f_initCustomSecret f_initSec)
{
    if (seed64 == 0)
        return XXH3_hashLong_128b_internal(input, len,
                                           XXH3_kSecret, sizeof(XXH3_kSecret),
                                           f_acc, f_scramble);
    {   XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE];
        f_initSec(secret, seed64);
        return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, sizeof(secret),
                                           f_acc, f_scramble);
    }
}

/*
 * It's important for performance that XXH3_hashLong is not inlined.
 */
XXH_NO_INLINE XXH128_hash_t
XXH3_hashLong_128b_withSeed(const void* input, size_t len,
                            XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen)
{
    (void)secret; (void)secretLen;
    return XXH3_hashLong_128b_withSeed_internal(input, len, seed64,
                XXH3_accumulate, XXH3_scrambleAcc, XXH3_initCustomSecret);
}

typedef XXH128_hash_t (*XXH3_hashLong128_f)(const void* XXH_RESTRICT, size_t,
                                            XXH64_hash_t, const void* XXH_RESTRICT, size_t);

XXH_FORCE_INLINE XXH128_hash_t
XXH3_128bits_internal(const void* input, size_t len,
                      XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen,
                      XXH3_hashLong128_f f_hl128)
{
    XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN);
    /*
     * If an action is to be taken if `secret` conditions are not respected,
     * it should be done here.
     * For now, it's a contract pre-condition.
     * Adding a check and a branch here would cost performance at every hash.
     */
    if (len <= 16)
        return XXH3_len_0to16_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, seed64);
    if (len <= 128)
        return XXH3_len_17to128_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64);
    if (len <= XXH3_MIDSIZE_MAX)
        return XXH3_len_129to240_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64);
    return f_hl128(input, len, seed64, secret, secretLen);
}


/* ===   Public XXH128 API   === */

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(XXH_NOESCAPE const void* input, size_t len)
{
    return XXH3_128bits_internal(input, len, 0,
                                 XXH3_kSecret, sizeof(XXH3_kSecret),
                                 XXH3_hashLong_128b_default);
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH128_hash_t
XXH3_128bits_withSecret(XXH_NOESCAPE const void* input, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize)
{
    return XXH3_128bits_internal(input, len, 0,
                                 (const xxh_u8*)secret, secretSize,
                                 XXH3_hashLong_128b_withSecret);
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH128_hash_t
XXH3_128bits_withSeed(XXH_NOESCAPE const void* input, size_t len, XXH64_hash_t seed)
{
    return XXH3_128bits_internal(input, len, seed,
                                 XXH3_kSecret, sizeof(XXH3_kSecret),
                                 XXH3_hashLong_128b_withSeed);
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH128_hash_t
XXH3_128bits_withSecretandSeed(XXH_NOESCAPE const void* input, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed)
{
    if (len <= XXH3_MIDSIZE_MAX)
        return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL);
    return XXH3_hashLong_128b_withSecret(input, len, seed, secret, secretSize);
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH128_hash_t
XXH128(XXH_NOESCAPE const void* input, size_t len, XXH64_hash_t seed)
{
    return XXH3_128bits_withSeed(input, len, seed);
}


/* ===   XXH3 128-bit streaming   === */
#ifndef XXH_NO_STREAM
/*
 * All initialization and update functions are identical to 64-bit streaming variant.
 * The only difference is the finalization routine.
 */

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH_errorcode
XXH3_128bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr)
{
    return XXH3_64bits_reset(statePtr);
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH_errorcode
XXH3_128bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize)
{
    return XXH3_64bits_reset_withSecret(statePtr, secret, secretSize);
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH_errorcode
XXH3_128bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed)
{
    return XXH3_64bits_reset_withSeed(statePtr, seed);
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH_errorcode
XXH3_128bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed)
{
    return XXH3_64bits_reset_withSecretandSeed(statePtr, secret, secretSize, seed);
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH_errorcode
XXH3_128bits_update(XXH_NOESCAPE XXH3_state_t* state, XXH_NOESCAPE const void* input, size_t len)
{
    return XXH3_64bits_update(state, input, len);
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (XXH_NOESCAPE const XXH3_state_t* state)
{
    const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret;
    if (state->totalLen > XXH3_MIDSIZE_MAX) {
        XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB];
        XXH3_digest_long(acc, state, secret);
        XXH_ASSERT(state->secretLimit + XXH_STRIPE_LEN >= sizeof(acc) + XXH_SECRET_MERGEACCS_START);
        {   XXH128_hash_t h128;
            h128.low64  = XXH3_mergeAccs(acc,
                                         secret + XXH_SECRET_MERGEACCS_START,
                                         (xxh_u64)state->totalLen * XXH_PRIME64_1);
            h128.high64 = XXH3_mergeAccs(acc,
                                         secret + state->secretLimit + XXH_STRIPE_LEN
                                                - sizeof(acc) - XXH_SECRET_MERGEACCS_START,
                                         ~((xxh_u64)state->totalLen * XXH_PRIME64_2));
            return h128;
        }
    }
    /* len <= XXH3_MIDSIZE_MAX : short code */
    if (state->seed)
        return XXH3_128bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed);
    return XXH3_128bits_withSecret(state->buffer, (size_t)(state->totalLen),
                                   secret, state->secretLimit + XXH_STRIPE_LEN);
}
#endif /* !XXH_NO_STREAM */
/* 128-bit utility functions */

#include <string.h>   /* memcmp, memcpy */

/* return : 1 is equal, 0 if different */
/*! @ingroup XXH3_family */
XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2)
{
    /* note : XXH128_hash_t is compact, it has no padding byte */
    return !(memcmp(&h1, &h2, sizeof(h1)));
}

/* This prototype is compatible with stdlib's qsort().
 * @return : >0 if *h128_1  > *h128_2
 *           <0 if *h128_1  < *h128_2
 *           =0 if *h128_1 == *h128_2  */
/*! @ingroup XXH3_family */
XXH_PUBLIC_API int XXH128_cmp(XXH_NOESCAPE const void* h128_1, XXH_NOESCAPE const void* h128_2)
{
    XXH128_hash_t const h1 = *(const XXH128_hash_t*)h128_1;
    XXH128_hash_t const h2 = *(const XXH128_hash_t*)h128_2;
    int const hcmp = (h1.high64 > h2.high64) - (h2.high64 > h1.high64);
    /* note : bets that, in most cases, hash values are different */
    if (hcmp) return hcmp;
    return (h1.low64 > h2.low64) - (h2.low64 > h1.low64);
}


/*======   Canonical representation   ======*/
/*! @ingroup XXH3_family */
XXH_PUBLIC_API void
XXH128_canonicalFromHash(XXH_NOESCAPE XXH128_canonical_t* dst, XXH128_hash_t hash)
{
    XXH_STATIC_ASSERT(sizeof(XXH128_canonical_t) == sizeof(XXH128_hash_t));
    if (XXH_CPU_LITTLE_ENDIAN) {
        hash.high64 = XXH_swap64(hash.high64);
        hash.low64  = XXH_swap64(hash.low64);
    }
    XXH_memcpy(dst, &hash.high64, sizeof(hash.high64));
    XXH_memcpy((char*)dst + sizeof(hash.high64), &hash.low64, sizeof(hash.low64));
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH128_hash_t
XXH128_hashFromCanonical(XXH_NOESCAPE const XXH128_canonical_t* src)
{
    XXH128_hash_t h;
    h.high64 = XXH_readBE64(src);
    h.low64  = XXH_readBE64(src->digest + 8);
    return h;
}



/* ==========================================
 * Secret generators
 * ==========================================
 */
#define XXH_MIN(x, y) (((x) > (y)) ? (y) : (x))

XXH_FORCE_INLINE void XXH3_combine16(void* dst, XXH128_hash_t h128)
{
    XXH_writeLE64( dst, XXH_readLE64(dst) ^ h128.low64 );
    XXH_writeLE64( (char*)dst+8, XXH_readLE64((char*)dst+8) ^ h128.high64 );
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API XXH_errorcode
XXH3_generateSecret(XXH_NOESCAPE void* secretBuffer, size_t secretSize, XXH_NOESCAPE const void* customSeed, size_t customSeedSize)
{
#if (XXH_DEBUGLEVEL >= 1)
    XXH_ASSERT(secretBuffer != NULL);
    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN);
#else
    /* production mode, assert() are disabled */
    if (secretBuffer == NULL) return XXH_ERROR;
    if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR;
#endif

    if (customSeedSize == 0) {
        customSeed = XXH3_kSecret;
        customSeedSize = XXH_SECRET_DEFAULT_SIZE;
    }
#if (XXH_DEBUGLEVEL >= 1)
    XXH_ASSERT(customSeed != NULL);
#else
    if (customSeed == NULL) return XXH_ERROR;
#endif

    /* Fill secretBuffer with a copy of customSeed - repeat as needed */
    {   size_t pos = 0;
        while (pos < secretSize) {
            size_t const toCopy = XXH_MIN((secretSize - pos), customSeedSize);
            memcpy((char*)secretBuffer + pos, customSeed, toCopy);
            pos += toCopy;
    }   }

    {   size_t const nbSeg16 = secretSize / 16;
        size_t n;
        XXH128_canonical_t scrambler;
        XXH128_canonicalFromHash(&scrambler, XXH128(customSeed, customSeedSize, 0));
        for (n=0; n<nbSeg16; n++) {
            XXH128_hash_t const h128 = XXH128(&scrambler, sizeof(scrambler), n);
            XXH3_combine16((char*)secretBuffer + n*16, h128);
        }
        /* last segment */
        XXH3_combine16((char*)secretBuffer + secretSize - 16, XXH128_hashFromCanonical(&scrambler));
    }
    return XXH_OK;
}

/*! @ingroup XXH3_family */
XXH_PUBLIC_API void
XXH3_generateSecret_fromSeed(XXH_NOESCAPE void* secretBuffer, XXH64_hash_t seed)
{
    XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE];
    XXH3_initCustomSecret(secret, seed);
    XXH_ASSERT(secretBuffer != NULL);
    memcpy(secretBuffer, secret, XXH_SECRET_DEFAULT_SIZE);
}



/* Pop our optimization override from above */
#if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \
  && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \
  && defined(__OPTIMIZE__) && XXH_SIZE_OPT <= 0 /* respect -O0 and -Os */
#  pragma GCC pop_options
#endif

#endif  /* XXH_NO_LONG_LONG */

#endif  /* XXH_NO_XXH3 */

/*!
 * @}
 */
#endif  /* XXH_IMPLEMENTATION */


#if defined (__cplusplus)
} /* extern "C" */
#endif

--- benchmark_other_languages/go/hash_functions/go.mod ---

module hash_functions

go 1.21.5


--- benchmark_other_languages/go/hash_functions/main.go ---

package main

import (
	"fmt"
	"hash/maphash"
	"strings"
	"time"
)

type void struct{}

var member void

func benchmark(corpus []string) {
	total := int64(0)
	k := make(map[string]void)
	v := make(map[uint64]void)
	v512 := make(map[uint64]void)

	for i := 0; i < 20; i++ {
		k = make(map[string]void)
		v = make(map[uint64]void)
		v512 = make(map[uint64]void)
		var hash maphash.Hash
		for _, key := range corpus {
			k[key] = member
			tik := time.Now().UnixNano()
			hash.WriteString(key)
			h := hash.Sum64()
			tok := time.Now().UnixNano()
			hash.Reset()
			v[h] = member
			v512[(h % 512)] = member
			total += tok - tik
		}
	}

	var min = 1000000
	var max = 0
	var sum = 0
	for key := range k {
		l := len(key)
		sum += l
		if l < min {
			min = l
		}
		if l > max {
			max = l
		}
	}
	avg := sum / len(k)

	avgTime := (float64(total) / 20.0) / float64(len(corpus))
	fmt.Printf("Avg time: %f, total elements: %d, unique elements: %d, collisions: %f, collisions mod 512: %f, keys min: %d, avg: %d, max: %d \n", avgTime, len(corpus), len(k), float64(len(k))/float64(len(v)), float64(len(k))/float64(len(v512)), min, avg, max)
}

func main() {
	corpus1 := "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque orci urna, pretium et porta ac, porttitor sit amet sem. Fusce sagittis lorem neque, vitae sollicitudin elit suscipit et. In interdum convallis nisl in ornare. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aliquam erat volutpat. Morbi mollis iaculis lectus ac tincidunt. Fusce nisi lacus, semper eu dignissim et, malesuada non mi. Sed euismod urna vel elit faucibus, eu bibendum ante fringilla. Curabitur tempus in turpis at mattis. Aliquam erat volutpat. Donec maximus elementum felis, sit amet dignissim augue tincidunt blandit. Aliquam fermentum, est eu mollis."
	corpus2 := "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains. But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains.But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection:"
	corpus3 := "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions! A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions! A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions!A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls"
	corpus4 := "Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое. Лежа на панцирнотвердой спине, он видел, стоило ему приподнять голову, свой коричневый, выпуклый, разделенный дугообразными чешуйками живот, на верхушке которого еле держалось готовое вот-вот окончательно сползти одеяло. Его многочисленные, убого тонкие по сравнению с остальным телом ножки беспомощно копошились у него перед глазами. «Что со мной случилось?» – подумал он. Это не было сном. Его комната, настоящая, разве что слишком маленькая, но обычная комната, мирно покоилась в своих четырех хорошо знакомых стенах. Над столом, где были разложены распакованные образцы сукон – Замза был коммивояжером, – висел портрет, который он недавно вырезал из иллюстрированного журнала и вставил в красивую золоченую рамку. На портрете была изображена дама в меховой шляпе и боа, она сидела очень прямо и протягивала зрителю тяжелую меховую муфту, в которой целиком исчезала ее рука. Затем взгляд Грегора устремился в окно, и пасмурная погода – слышно было, как по жести подоконника стучат капли дождя – привела его и вовсе в грустное настроение. «Хорошо бы еще немного поспать и забыть всю эту чепуху», – подумал он, но это было совершенно неосуществимо, он привык спать на правом боку, а в теперешнем своем состоянии он никак не мог принять этого положения. С какой бы силой ни поворачивался он на правый бок, он неизменно сваливался опять на спину. Закрыв глаза, чтобы не видеть своих барахтающихся ног, он проделал это добрую сотню раз и отказался от этих попыток только тогда, когда почувствовал какую-то неведомую дотоле, тупую и слабую боль в боку. «Ах ты, господи, – подумал он, – какую я выбрал хлопотную профессию! Изо дня в день в разъездах. Деловых волнений куда больше, чем на месте, в торговом доме, а кроме того, изволь терпеть тяготы дороги, думай о расписании поездов, мирись с плохим, нерегулярным питанием, завязывай со все новыми и новыми людьми недолгие, никогда не бывающие сердечными отношения. Черт бы побрал все это!» Он почувствовал вверху живота легкий зуд; медленно подвинулся на спине к прутьям кровати, чтобы удобнее было поднять голову; нашел зудевшее место, сплошь покрытое, как оказалось, белыми непонятными точечками; хотел было ощупать это место одной из ножек, но сразу отдернул ее, ибо даже простое прикосновение вызвало у него, Грегора, озноб. Он соскользнул в прежнее свое положение. «От этого раннего вставания, – подумал он, – можно совсем обезуметь. Человек должен высыпаться. Другие коммивояжеры живут, как одалиски. Когда я, например, среди дня возвращаюсь в гостиницу, чтобы переписать полученные заказы, эти господа только завтракают. А осмелься я вести себя так, мои хозяин выгнал бы меня сразу. Кто знает, впрочем, может быть, это было бы даже очень хорошо для меня. Если бы я не сдерживался ради родителей, я бы давно заявил об уходе, я бы подошел к своему хозяину и выложил ему все, что о нем думаю. Он бы так и свалился с конторки! Странная у него манера – садиться на конторку и с ее высоты разговаривать со служащим, который вдобавок вынужден подойти вплотную к конторке из-за того, что хозяин туг на ухо. Однако надежда еще не совсем потеряна: как только я накоплю денег, чтобы выплатить долг моих родителей – на это уйдет еще лет пять-шесть, – я так и поступлю. Тут-то мы и распрощаемся раз и навсегда. А пока что надо подниматься, мой поезд отходит в пять». И он взглянул на будильник, который тикал на сундуке. «Боже правый!» – подумал он. Было половина седьмого, и стрелки спокойно двигались дальше, было даже больше половины, без малого уже три четверти. Неужели будильник не звонил? С кровати было видно, что он поставлен правильно, на четыре часа; и он, несомненно, звонил. Но как можно было спокойно спать под этот сотрясающий мебель трезвон? Ну, спал-то он неспокойно, но, видимо, крепко. Однако что делать теперь? Следующий поезд уходит в семь часов; чтобы поспеть на него, он должен отчаянно торопиться, а набор образцов еще не упакован, да и сам он отнюдь не чувствует себя свежим и легким на подъем. И даже поспей он на поезд, хозяйского разноса ему все равно не избежать – ведь рассыльный торгового дома дежурил у пятичасового поезда и давно доложил о его, Грегора, опоздании. Рассыльный, человек бесхарактерный и неумный, был ставленником хозяина. А что, если сказаться больным? Но это было бы крайне неприятно и показалось бы подозрительным, ибо за пятилетнюю свою службу Грегор ни разу еще не болел. Хозяин, конечно, привел бы врача больничной кассы и стал попрекать родителей сыном-лентяем, отводя любые возражения ссылкой на этого врача, по мнению которого все люди на свете совершенно здоровы и только не любят работать. И разве в данном случае он был бы так уж неправ? Если не считать сонливости, действительно странной после такого долгого сна, Грегор и в самом деле чувствовал себя превосходно и был даже чертовски голоден.Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое. Лежа на панцирнотвердой спине, он видел, стоило ему приподнять голову, свой коричневый, выпуклый, разделенный дугообразными чешуйками живот, на верхушке которого еле держалось готовое вот-вот окончательно сползти одеяло. Его многочисленные, убого тонкие по сравнению с остальным телом ножки беспомощно копошились у него перед глазами. «Что со мной случилось?» – подумал он. Это не было сном. Его комната, настоящая, разве что слишком маленькая, но обычная комната, мирно покоилась в своих четырех хорошо знакомых стенах. Над столом, где были разложены распакованные образцы сукон – Замза был коммивояжером, – висел портрет, который он недавно вырезал из иллюстрированного журнала и вставил в красивую золоченую рамку. На портрете была изображена дама в меховой шляпе и боа, она сидела очень прямо и протягивала зрителю тяжелую меховую муфту, в которой целиком исчезала ее рука. Затем взгляд Грегора устремился в окно, и пасмурная погода – слышно было, как по жести подоконника стучат капли дождя – привела его и вовсе в грустное настроение. «Хорошо бы еще немного поспать и забыть всю эту чепуху», – подумал он, но это было совершенно неосуществимо, он привык спать на правом боку, а в теперешнем своем состоянии он никак не мог принять этого положения. С какой бы силой ни поворачивался он на правый бок, он неизменно сваливался опять на спину."
	corpus5 := "Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch. Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch. Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch.Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen"
	corpus6 := "米くを舵4物委らご氏松ハナテフ月関ソ時平ふいの博情れじフ牟万い元56園フメヤオ試図ロツヤ未備王こと傷喫羅踊んゆし。栃ユヱオ書著作ユソツロ英祉業ア大課ご権質フべ空8午キ切軟づン著郎そゃす格町採ヱオマコ処8付国ムハチア究表でなだ際無ロミヱ地兵ぴげ庭体すク発抜爆位や。楽富むゆず盛航カナセ携代ハ本高きた員59今骸ンラえぜ城解イケ穴訴ぽぎ属住ヤケトヌ抱点ト広注厚でて。 国リ出難セユメ軍手ヘカウ画形サヲシ猛85用ヲキミ心死よしと身処ケヨミオ教主ーぽ事業んく字国たさょ図能シミスヤ社8板ル岡世58次戒知院んれり。市メ誘根カ数問禁竹ゃれえみ給辺のでみき今二ぎさ裕止過こクすと無32郎所ラた生展ヌヘス成度慣葬勇厘ばてか。室ゃ下携疲ム色権がぽりっ銃週ノオ姫千テム健蔵い研手ッ放容ル告属め旅側26企サノヨ宅都福ぞ通待ちぴね種脳イど労希望義通むン。 罰しい続負せ著低たル異師ユハワ東添質コチ転集ルヤ雇聴約ヒ前統らた情厳ゆさでや真胸や有披暑棚豆ゆぼたけ。盛ワセロナ情競クるっわ講3音ずをせ少地めしぜょ手63明視れに判企ヒヌエソ求総58特本ね井比ユラキ禁頭馬るゅリす能率率かがさわ。葉サソ医郡ヱヘソ労帰ナケスミ救写ワヘ株審ネヒニミ安逮イ人画ラ涯車はラ極騒りなド件5級ンかふー劇41著ぱぐ凱討だ文世ぶづどま界善魅マ渓経競融れがや。 連ーぜらご模分ッ視外ばフく運発群ほぼづ育越一ほごクけ案募ヲイソ治会イせフ製君ぜた漢村1変リヒ構5際ツ御文ヲ臭入さドぼ代書ハケ引技ろみれ回観注倉徹ぱ。論ラづ海要サ情座ゃり齢宣ラモエ芸化エマホ覧催回ら戦69本外ト葬岳な政画か連針ぴリフず。約ル闘辺ぽ経2応掲ホサアラ塾小コラ画決クノオ上室レヌヱ勝逮ぜるえむ責豊チノ明意ひけ訟6碁草メタチエ財午召喝塊む。 決めでわ名金つけレわ続人県約ぽぼす尾腹ユサ戦載リシ護賀レモフツ重涯ニ治者むんっみ職更カタチレ提話2何ワ責東まけげふ能政ヌ供禁がびてわ提改倶れめ。読み担後ぽ安加ぎ論鹿ツ統最お気麻月つじもあ竜思いろめ判必満理トコ文連ムイウハ寄串ざほびー。文ゆこっ向27年メイ便能ノセヲ待1王スねたゆ伝派んね点過カト治読よにきべ使人スシ都言え阻8割べづえみ注引敷的岳犠眠どそ。 学用イだ医客開ロ供界もぞだ実隆モイヌ務坂ナコヲ権野ろづ初場ぱ低会づぱじ新倒コ化政レ止奮浸猪ッわえづ。形いやリ要帰ほまむだ業領スル必打さ島14巻リ集日ネヘホタ面幅ち写上そぴ円図ムタコモ報使イわざと会催ヤヲ康証をドぶレ盤岡ホハツ作29管しをめ公問懐蓄っさ。来ゆぼあぱ投秋シ語右ぐ身靖かば辛握捕家記ヘワ神岐囲づ毘観メテクツ政73夕罪57需93誌飲査仁さ。 変レめ束球よんま会特ヱコ聞重だ史純ーどる件32浦レぴよゃ上強ネラリロ査従セユヤ専棋光レ作表ひぶ予正ぜーな誉確フス函6報円ス進治ね能営済否雄でわょ。42生型ば着続ア短実ぎおめび前環闘ラヤヲル診均っとにの声公トヱテマ整試椅情久妊舌頃ざとっく。品キチトテ阿国ラら受87世ヲフセリ川86個ーょぼげ危子ヘレカメ無会ぱかへ事通んかて電条ロツ徴商ぶぞそを居暑メ害広せもがり禁応レミヲ応響割壮憶はぱ。 千れンが織財メニ況界ネトレミ学豊フオホシ近月レたやご的罪ょな菱技ちる警栗エセ提89林危氷48参ア説森クキヒヱ薬社ホコエリ負和ルび紀下ケミイ掲歳特ごず扱底ク護木連ちクを各形ばすか。変ぱなれ町7融ヌ街準以タユヘム質裕ぶで遺語俊ぎずょ事金文キ写多山ーゆに歩帯すで会世クぜよ論写ヲ達71林危氷5間続ぎぜび高怠す。 係8青け応著ミ戦条ナヘネカ思79未ぎ算伊をゃ泉人ーづ需説っ畑鹿27軽ラソツ権2促千護ルロナカ開国ケ暴嶋ご池表だ。佐フナ訪麻はてせば勝効をあ医戦画とさわぴ者両すいあ並来んば載食ぴ件友頂業へえぞ魚祝ネラ聞率スコリケ始全ンこび夫出ドふ今布うぎふゅ実克即哉循やしんな。 暮す備54依紀てッん末刊と柔称むてス無府ケイ変壌をぱ汁連フマス海世ヌ中負知問ナヘケ純推ひ読着ヒ言若私軽れ。掲けフむ王本オコ線人をっさ必和断セソヲハ図芸ちかな防長りぶは投新意相ツ並5余セ職岳ぞ端古空援そ。森ヨエチ題5東っ自兄ち暴5近鹿横ト的京ハ安氷ナキ深際ぎ並節くスむの権工ほルせ京49効タムチ処三ぞぴラ済国ずっ文経ヘトミ水分準そが。"
	corpus7 := "AbortMultipartUpload CompleteMultipartUpload CopyObject CreateBucket CreateMultipartUpload DeleteBucket DeleteBucketAnalyticsConfiguration DeleteBucketCors DeleteBucketEncryption DeleteBucketIntelligentTieringConfiguration DeleteBucketInventoryConfiguration DeleteBucketLifecycle DeleteBucketMetricsConfiguration DeleteBucketOwnershipControls DeleteBucketPolicy DeleteBucketReplication DeleteBucketTagging DeleteBucketWebsite DeleteObject DeleteObjects DeleteObjectTagging DeletePublicAccessBlock GetBucketAccelerateConfiguration GetBucketAcl GetBucketAnalyticsConfiguration GetBucketCors GetBucketEncryption GetBucketIntelligentTieringConfiguration GetBucketInventoryConfiguration GetBucketLifecycle GetBucketLifecycleConfiguration GetBucketLocation GetBucketLogging GetBucketMetricsConfiguration GetBucketNotification GetBucketNotificationConfiguration GetBucketOwnershipControls GetBucketPolicy GetBucketPolicyStatus GetBucketReplication GetBucketRequestPayment GetBucketTagging GetBucketVersioning GetBucketWebsite GetObject GetObjectAcl GetObjectAttributes GetObjectLegalHold GetObjectLockConfiguration GetObjectRetention GetObjectTagging GetObjectTorrent GetPublicAccessBlock HeadBucket HeadObject ListBucketAnalyticsConfigurations ListBucketIntelligentTieringConfigurations ListBucketInventoryConfigurations ListBucketMetricsConfigurations ListBuckets ListMultipartUploads ListObjects ListObjectsV2 ListObjectVersions ListParts PutBucketAccelerateConfiguration PutBucketAcl PutBucketAnalyticsConfiguration PutBucketCors PutBucketEncryption PutBucketIntelligentTieringConfiguration PutBucketInventoryConfiguration PutBucketLifecycle PutBucketLifecycleConfiguration PutBucketLogging PutBucketMetricsConfiguration PutBucketNotification PutBucketNotificationConfiguration PutBucketOwnershipControls PutBucketPolicy PutBucketReplication PutBucketRequestPayment PutBucketTagging PutBucketVersioning PutBucketWebsite PutObject PutObjectAcl PutObjectLegalHold PutObjectLockConfiguration PutObjectRetention PutObjectTagging PutPublicAccessBlock RestoreObject SelectObjectContent UploadPart UploadPartCopy WriteGetObjectResponse CreateAccessPoint CreateAccessPointForObjectLambda CreateBucket CreateJob CreateMultiRegionAccessPoint DeleteAccessPoint DeleteAccessPointForObjectLambda DeleteAccessPointPolicy DeleteAccessPointPolicyForObjectLambda DeleteBucket DeleteBucketLifecycleConfiguration DeleteBucketPolicy DeleteBucketReplication DeleteBucketTagging DeleteJobTagging DeleteMultiRegionAccessPoint DeletePublicAccessBlock DeleteStorageLensConfiguration DeleteStorageLensConfigurationTagging DescribeJob DescribeMultiRegionAccessPointOperation GetAccessPoint GetAccessPointConfigurationForObjectLambda GetAccessPointForObjectLambda GetAccessPointPolicy GetAccessPointPolicyForObjectLambda GetAccessPointPolicyStatus GetAccessPointPolicyStatusForObjectLambda GetBucket GetBucketLifecycleConfiguration GetBucketPolicy GetBucketReplication GetBucketTagging GetBucketVersioning GetJobTagging GetMultiRegionAccessPoint GetMultiRegionAccessPointPolicy GetMultiRegionAccessPointPolicyStatus GetMultiRegionAccessPointRoutes GetPublicAccessBlock GetStorageLensConfiguration GetStorageLensConfigurationTagging ListAccessPoints ListAccessPointsForObjectLambda ListJobs ListMultiRegionAccessPoints ListRegionalBuckets ListStorageLensConfigurations PutAccessPointConfigurationForObjectLambda PutAccessPointPolicy PutAccessPointPolicyForObjectLambda PutBucketLifecycleConfiguration PutBucketPolicy PutBucketReplication PutBucketTagging PutBucketVersioning PutJobTagging PutMultiRegionAccessPointPolicy PutPublicAccessBlock PutStorageLensConfiguration PutStorageLensConfigurationTagging SubmitMultiRegionAccessPointRoutes UpdateJobPriority UpdateJobStatus"

	corps := []string{corpus1, corpus2, corpus3, corpus4, corpus5, corpus6, corpus7}
	for _, corpus := range corps {
		keys := strings.Split(corpus, " ")
		benchmark(keys)
	}
}


--- benchmark_other_languages/js/hash_functions/.gitignore ---

/node_modules

--- benchmark_other_languages/js/hash_functions/README.md ---

First execute `npm install` to fetch the dependencies and then execute `node benchmark.js`.


--- benchmark_other_languages/js/hash_functions/benchmark.js ---

const { wyhash, wyhash_str } = require('wyhash');
const XXH = require("xxhashjs")

function len(o) {
    return Object.keys(o).length;
}

function benchmark_xxhash(corpus) {
    var total = 0;
    for (let i = 0; i < 20; i++) {
        var k = {};
        var v = {};
        var v512 = {};
        corpus.forEach(key => {
            k[key] = true;
            const tik = process.hrtime();
            const h = XXH.h64( key, 0xABCD ).toNumber();
            const tok = process.hrtime(tik);
            total += tok[1];
            v[h] = true;
            v512[(h % 512)] = true; 
        });  
    }
        
    var min = 10000000;
    var max = 0;
    var sum = 0;
    Object.keys(k).forEach (key => {
        const l = key.length;
        sum += l;
        if (l < min) {min = l}
        if (l > max) {max = l}
    });
        
    const avg = sum / len(k);
    console.log(`Avg time xxHash: ${((total / 20.0) / corpus.length)}, total elements: ${corpus.length}, unique elements: ${len(k)}, collisions: ${(len(k) / len(v))}, collisions % 512: ${(len(k) / len(v512))}, keys min: ${min}, avg: ${avg}, max: ${max}`);
}

function benchmark_wyhash(corpus) {
    var total = 0;
    for (let i = 0; i < 20; i++) {
        var k = {};
        var v = {};
        var v512 = {};
        corpus.forEach(key => {
            k[key] = true;
            const tik = process.hrtime();
            const h = wyhash_str(key, 0n);
            const tok = process.hrtime(tik);
            total += tok[1];
            v[h] = true;
            v512[(h % 512n)] = true; 
        });  
    }
        
    var min = 10000000;
    var max = 0;
    var sum = 0;
    Object.keys(k).forEach (key => {
        const l = key.length;
        sum += l;
        if (l < min) {min = l}
        if (l > max) {max = l}
    });
        
    const avg = sum / len(k);
    console.log(`Avg time WyHash: ${((total / 20.0) / corpus.length)}, total elements: ${corpus.length}, unique elements: ${len(k)}, collisions: ${(len(k) / len(v))}, collisions % 512: ${(len(k) / len(v512))}, keys min: ${min}, avg: ${avg}, max: ${max}`);
}

function main() {
    const corpus1 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque orci urna, pretium et porta ac, porttitor sit amet sem. Fusce sagittis lorem neque, vitae sollicitudin elit suscipit et. In interdum convallis nisl in ornare. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aliquam erat volutpat. Morbi mollis iaculis lectus ac tincidunt. Fusce nisi lacus, semper eu dignissim et, malesuada non mi. Sed euismod urna vel elit faucibus, eu bibendum ante fringilla. Curabitur tempus in turpis at mattis. Aliquam erat volutpat. Donec maximus elementum felis, sit amet dignissim augue tincidunt blandit. Aliquam fermentum, est eu mollis.".split(" ")
    const corpus2 = "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains. But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains.But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection:".split(" ");
    const corpus3 = "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions! A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions! A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions!A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls".split(" ");
    const corpus4 = "Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое. Лежа на панцирнотвердой спине, он видел, стоило ему приподнять голову, свой коричневый, выпуклый, разделенный дугообразными чешуйками живот, на верхушке которого еле держалось готовое вот-вот окончательно сползти одеяло. Его многочисленные, убого тонкие по сравнению с остальным телом ножки беспомощно копошились у него перед глазами. «Что со мной случилось?» – подумал он. Это не было сном. Его комната, настоящая, разве что слишком маленькая, но обычная комната, мирно покоилась в своих четырех хорошо знакомых стенах. Над столом, где были разложены распакованные образцы сукон – Замза был коммивояжером, – висел портрет, который он недавно вырезал из иллюстрированного журнала и вставил в красивую золоченую рамку. На портрете была изображена дама в меховой шляпе и боа, она сидела очень прямо и протягивала зрителю тяжелую меховую муфту, в которой целиком исчезала ее рука. Затем взгляд Грегора устремился в окно, и пасмурная погода – слышно было, как по жести подоконника стучат капли дождя – привела его и вовсе в грустное настроение. «Хорошо бы еще немного поспать и забыть всю эту чепуху», – подумал он, но это было совершенно неосуществимо, он привык спать на правом боку, а в теперешнем своем состоянии он никак не мог принять этого положения. С какой бы силой ни поворачивался он на правый бок, он неизменно сваливался опять на спину. Закрыв глаза, чтобы не видеть своих барахтающихся ног, он проделал это добрую сотню раз и отказался от этих попыток только тогда, когда почувствовал какую-то неведомую дотоле, тупую и слабую боль в боку. «Ах ты, господи, – подумал он, – какую я выбрал хлопотную профессию! Изо дня в день в разъездах. Деловых волнений куда больше, чем на месте, в торговом доме, а кроме того, изволь терпеть тяготы дороги, думай о расписании поездов, мирись с плохим, нерегулярным питанием, завязывай со все новыми и новыми людьми недолгие, никогда не бывающие сердечными отношения. Черт бы побрал все это!» Он почувствовал вверху живота легкий зуд; медленно подвинулся на спине к прутьям кровати, чтобы удобнее было поднять голову; нашел зудевшее место, сплошь покрытое, как оказалось, белыми непонятными точечками; хотел было ощупать это место одной из ножек, но сразу отдернул ее, ибо даже простое прикосновение вызвало у него, Грегора, озноб. Он соскользнул в прежнее свое положение. «От этого раннего вставания, – подумал он, – можно совсем обезуметь. Человек должен высыпаться. Другие коммивояжеры живут, как одалиски. Когда я, например, среди дня возвращаюсь в гостиницу, чтобы переписать полученные заказы, эти господа только завтракают. А осмелься я вести себя так, мои хозяин выгнал бы меня сразу. Кто знает, впрочем, может быть, это было бы даже очень хорошо для меня. Если бы я не сдерживался ради родителей, я бы давно заявил об уходе, я бы подошел к своему хозяину и выложил ему все, что о нем думаю. Он бы так и свалился с конторки! Странная у него манера – садиться на конторку и с ее высоты разговаривать со служащим, который вдобавок вынужден подойти вплотную к конторке из-за того, что хозяин туг на ухо. Однако надежда еще не совсем потеряна: как только я накоплю денег, чтобы выплатить долг моих родителей – на это уйдет еще лет пять-шесть, – я так и поступлю. Тут-то мы и распрощаемся раз и навсегда. А пока что надо подниматься, мой поезд отходит в пять». И он взглянул на будильник, который тикал на сундуке. «Боже правый!» – подумал он. Было половина седьмого, и стрелки спокойно двигались дальше, было даже больше половины, без малого уже три четверти. Неужели будильник не звонил? С кровати было видно, что он поставлен правильно, на четыре часа; и он, несомненно, звонил. Но как можно было спокойно спать под этот сотрясающий мебель трезвон? Ну, спал-то он неспокойно, но, видимо, крепко. Однако что делать теперь? Следующий поезд уходит в семь часов; чтобы поспеть на него, он должен отчаянно торопиться, а набор образцов еще не упакован, да и сам он отнюдь не чувствует себя свежим и легким на подъем. И даже поспей он на поезд, хозяйского разноса ему все равно не избежать – ведь рассыльный торгового дома дежурил у пятичасового поезда и давно доложил о его, Грегора, опоздании. Рассыльный, человек бесхарактерный и неумный, был ставленником хозяина. А что, если сказаться больным? Но это было бы крайне неприятно и показалось бы подозрительным, ибо за пятилетнюю свою службу Грегор ни разу еще не болел. Хозяин, конечно, привел бы врача больничной кассы и стал попрекать родителей сыном-лентяем, отводя любые возражения ссылкой на этого врача, по мнению которого все люди на свете совершенно здоровы и только не любят работать. И разве в данном случае он был бы так уж неправ? Если не считать сонливости, действительно странной после такого долгого сна, Грегор и в самом деле чувствовал себя превосходно и был даже чертовски голоден.Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое. Лежа на панцирнотвердой спине, он видел, стоило ему приподнять голову, свой коричневый, выпуклый, разделенный дугообразными чешуйками живот, на верхушке которого еле держалось готовое вот-вот окончательно сползти одеяло. Его многочисленные, убого тонкие по сравнению с остальным телом ножки беспомощно копошились у него перед глазами. «Что со мной случилось?» – подумал он. Это не было сном. Его комната, настоящая, разве что слишком маленькая, но обычная комната, мирно покоилась в своих четырех хорошо знакомых стенах. Над столом, где были разложены распакованные образцы сукон – Замза был коммивояжером, – висел портрет, который он недавно вырезал из иллюстрированного журнала и вставил в красивую золоченую рамку. На портрете была изображена дама в меховой шляпе и боа, она сидела очень прямо и протягивала зрителю тяжелую меховую муфту, в которой целиком исчезала ее рука. Затем взгляд Грегора устремился в окно, и пасмурная погода – слышно было, как по жести подоконника стучат капли дождя – привела его и вовсе в грустное настроение. «Хорошо бы еще немного поспать и забыть всю эту чепуху», – подумал он, но это было совершенно неосуществимо, он привык спать на правом боку, а в теперешнем своем состоянии он никак не мог принять этого положения. С какой бы силой ни поворачивался он на правый бок, он неизменно сваливался опять на спину.".split(" ");
    const corpus5 = "Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch. Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch. Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch.Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen".split(" ");
    const corpus6 = "米くを舵4物委らご氏松ハナテフ月関ソ時平ふいの博情れじフ牟万い元56園フメヤオ試図ロツヤ未備王こと傷喫羅踊んゆし。栃ユヱオ書著作ユソツロ英祉業ア大課ご権質フべ空8午キ切軟づン著郎そゃす格町採ヱオマコ処8付国ムハチア究表でなだ際無ロミヱ地兵ぴげ庭体すク発抜爆位や。楽富むゆず盛航カナセ携代ハ本高きた員59今骸ンラえぜ城解イケ穴訴ぽぎ属住ヤケトヌ抱点ト広注厚でて。 国リ出難セユメ軍手ヘカウ画形サヲシ猛85用ヲキミ心死よしと身処ケヨミオ教主ーぽ事業んく字国たさょ図能シミスヤ社8板ル岡世58次戒知院んれり。市メ誘根カ数問禁竹ゃれえみ給辺のでみき今二ぎさ裕止過こクすと無32郎所ラた生展ヌヘス成度慣葬勇厘ばてか。室ゃ下携疲ム色権がぽりっ銃週ノオ姫千テム健蔵い研手ッ放容ル告属め旅側26企サノヨ宅都福ぞ通待ちぴね種脳イど労希望義通むン。 罰しい続負せ著低たル異師ユハワ東添質コチ転集ルヤ雇聴約ヒ前統らた情厳ゆさでや真胸や有披暑棚豆ゆぼたけ。盛ワセロナ情競クるっわ講3音ずをせ少地めしぜょ手63明視れに判企ヒヌエソ求総58特本ね井比ユラキ禁頭馬るゅリす能率率かがさわ。葉サソ医郡ヱヘソ労帰ナケスミ救写ワヘ株審ネヒニミ安逮イ人画ラ涯車はラ極騒りなド件5級ンかふー劇41著ぱぐ凱討だ文世ぶづどま界善魅マ渓経競融れがや。 連ーぜらご模分ッ視外ばフく運発群ほぼづ育越一ほごクけ案募ヲイソ治会イせフ製君ぜた漢村1変リヒ構5際ツ御文ヲ臭入さドぼ代書ハケ引技ろみれ回観注倉徹ぱ。論ラづ海要サ情座ゃり齢宣ラモエ芸化エマホ覧催回ら戦69本外ト葬岳な政画か連針ぴリフず。約ル闘辺ぽ経2応掲ホサアラ塾小コラ画決クノオ上室レヌヱ勝逮ぜるえむ責豊チノ明意ひけ訟6碁草メタチエ財午召喝塊む。 決めでわ名金つけレわ続人県約ぽぼす尾腹ユサ戦載リシ護賀レモフツ重涯ニ治者むんっみ職更カタチレ提話2何ワ責東まけげふ能政ヌ供禁がびてわ提改倶れめ。読み担後ぽ安加ぎ論鹿ツ統最お気麻月つじもあ竜思いろめ判必満理トコ文連ムイウハ寄串ざほびー。文ゆこっ向27年メイ便能ノセヲ待1王スねたゆ伝派んね点過カト治読よにきべ使人スシ都言え阻8割べづえみ注引敷的岳犠眠どそ。 学用イだ医客開ロ供界もぞだ実隆モイヌ務坂ナコヲ権野ろづ初場ぱ低会づぱじ新倒コ化政レ止奮浸猪ッわえづ。形いやリ要帰ほまむだ業領スル必打さ島14巻リ集日ネヘホタ面幅ち写上そぴ円図ムタコモ報使イわざと会催ヤヲ康証をドぶレ盤岡ホハツ作29管しをめ公問懐蓄っさ。来ゆぼあぱ投秋シ語右ぐ身靖かば辛握捕家記ヘワ神岐囲づ毘観メテクツ政73夕罪57需93誌飲査仁さ。 変レめ束球よんま会特ヱコ聞重だ史純ーどる件32浦レぴよゃ上強ネラリロ査従セユヤ専棋光レ作表ひぶ予正ぜーな誉確フス函6報円ス進治ね能営済否雄でわょ。42生型ば着続ア短実ぎおめび前環闘ラヤヲル診均っとにの声公トヱテマ整試椅情久妊舌頃ざとっく。品キチトテ阿国ラら受87世ヲフセリ川86個ーょぼげ危子ヘレカメ無会ぱかへ事通んかて電条ロツ徴商ぶぞそを居暑メ害広せもがり禁応レミヲ応響割壮憶はぱ。 千れンが織財メニ況界ネトレミ学豊フオホシ近月レたやご的罪ょな菱技ちる警栗エセ提89林危氷48参ア説森クキヒヱ薬社ホコエリ負和ルび紀下ケミイ掲歳特ごず扱底ク護木連ちクを各形ばすか。変ぱなれ町7融ヌ街準以タユヘム質裕ぶで遺語俊ぎずょ事金文キ写多山ーゆに歩帯すで会世クぜよ論写ヲ達71林危氷5間続ぎぜび高怠す。 係8青け応著ミ戦条ナヘネカ思79未ぎ算伊をゃ泉人ーづ需説っ畑鹿27軽ラソツ権2促千護ルロナカ開国ケ暴嶋ご池表だ。佐フナ訪麻はてせば勝効をあ医戦画とさわぴ者両すいあ並来んば載食ぴ件友頂業へえぞ魚祝ネラ聞率スコリケ始全ンこび夫出ドふ今布うぎふゅ実克即哉循やしんな。 暮す備54依紀てッん末刊と柔称むてス無府ケイ変壌をぱ汁連フマス海世ヌ中負知問ナヘケ純推ひ読着ヒ言若私軽れ。掲けフむ王本オコ線人をっさ必和断セソヲハ図芸ちかな防長りぶは投新意相ツ並5余セ職岳ぞ端古空援そ。森ヨエチ題5東っ自兄ち暴5近鹿横ト的京ハ安氷ナキ深際ぎ並節くスむの権工ほルせ京49効タムチ処三ぞぴラ済国ずっ文経ヘトミ水分準そが。".split(" ");
    const corpus7 = "AbortMultipartUpload CompleteMultipartUpload CopyObject CreateBucket CreateMultipartUpload DeleteBucket DeleteBucketAnalyticsConfiguration DeleteBucketCors DeleteBucketEncryption DeleteBucketIntelligentTieringConfiguration DeleteBucketInventoryConfiguration DeleteBucketLifecycle DeleteBucketMetricsConfiguration DeleteBucketOwnershipControls DeleteBucketPolicy DeleteBucketReplication DeleteBucketTagging DeleteBucketWebsite DeleteObject DeleteObjects DeleteObjectTagging DeletePublicAccessBlock GetBucketAccelerateConfiguration GetBucketAcl GetBucketAnalyticsConfiguration GetBucketCors GetBucketEncryption GetBucketIntelligentTieringConfiguration GetBucketInventoryConfiguration GetBucketLifecycle GetBucketLifecycleConfiguration GetBucketLocation GetBucketLogging GetBucketMetricsConfiguration GetBucketNotification GetBucketNotificationConfiguration GetBucketOwnershipControls GetBucketPolicy GetBucketPolicyStatus GetBucketReplication GetBucketRequestPayment GetBucketTagging GetBucketVersioning GetBucketWebsite GetObject GetObjectAcl GetObjectAttributes GetObjectLegalHold GetObjectLockConfiguration GetObjectRetention GetObjectTagging GetObjectTorrent GetPublicAccessBlock HeadBucket HeadObject ListBucketAnalyticsConfigurations ListBucketIntelligentTieringConfigurations ListBucketInventoryConfigurations ListBucketMetricsConfigurations ListBuckets ListMultipartUploads ListObjects ListObjectsV2 ListObjectVersions ListParts PutBucketAccelerateConfiguration PutBucketAcl PutBucketAnalyticsConfiguration PutBucketCors PutBucketEncryption PutBucketIntelligentTieringConfiguration PutBucketInventoryConfiguration PutBucketLifecycle PutBucketLifecycleConfiguration PutBucketLogging PutBucketMetricsConfiguration PutBucketNotification PutBucketNotificationConfiguration PutBucketOwnershipControls PutBucketPolicy PutBucketReplication PutBucketRequestPayment PutBucketTagging PutBucketVersioning PutBucketWebsite PutObject PutObjectAcl PutObjectLegalHold PutObjectLockConfiguration PutObjectRetention PutObjectTagging PutPublicAccessBlock RestoreObject SelectObjectContent UploadPart UploadPartCopy WriteGetObjectResponse CreateAccessPoint CreateAccessPointForObjectLambda CreateBucket CreateJob CreateMultiRegionAccessPoint DeleteAccessPoint DeleteAccessPointForObjectLambda DeleteAccessPointPolicy DeleteAccessPointPolicyForObjectLambda DeleteBucket DeleteBucketLifecycleConfiguration DeleteBucketPolicy DeleteBucketReplication DeleteBucketTagging DeleteJobTagging DeleteMultiRegionAccessPoint DeletePublicAccessBlock DeleteStorageLensConfiguration DeleteStorageLensConfigurationTagging DescribeJob DescribeMultiRegionAccessPointOperation GetAccessPoint GetAccessPointConfigurationForObjectLambda GetAccessPointForObjectLambda GetAccessPointPolicy GetAccessPointPolicyForObjectLambda GetAccessPointPolicyStatus GetAccessPointPolicyStatusForObjectLambda GetBucket GetBucketLifecycleConfiguration GetBucketPolicy GetBucketReplication GetBucketTagging GetBucketVersioning GetJobTagging GetMultiRegionAccessPoint GetMultiRegionAccessPointPolicy GetMultiRegionAccessPointPolicyStatus GetMultiRegionAccessPointRoutes GetPublicAccessBlock GetStorageLensConfiguration GetStorageLensConfigurationTagging ListAccessPoints ListAccessPointsForObjectLambda ListJobs ListMultiRegionAccessPoints ListRegionalBuckets ListStorageLensConfigurations PutAccessPointConfigurationForObjectLambda PutAccessPointPolicy PutAccessPointPolicyForObjectLambda PutBucketLifecycleConfiguration PutBucketPolicy PutBucketReplication PutBucketTagging PutBucketVersioning PutJobTagging PutMultiRegionAccessPointPolicy PutPublicAccessBlock PutStorageLensConfiguration PutStorageLensConfigurationTagging SubmitMultiRegionAccessPointRoutes UpdateJobPriority UpdateJobStatus".split(" ");
    // First corpus1 is inserted twice because the first run is always slower
    const corps = [corpus1, corpus1, corpus2, corpus3, corpus4, corpus5, corpus6, corpus7];
    for (let index = 0; index < corps.length; index++) {
        let corpus = corps[index];
        benchmark_wyhash(corpus);
        benchmark_xxhash(corpus);
    }
}

main()

--- benchmark_other_languages/python/hash_functions/benchmark.py ---

from time import time_ns

def benchmark(corpus: list[str]):
    total = 0
    for _ in range(20):
        k = set()
        v = set()
        v512 = set()
        for key in corpus:
            k.add(key)
            tik = time_ns()
            h = hash(key)
            tok = time_ns()
            total += (tok - tik)
            v.add(h)
            v512.add(h % 512)
    min = 10000000
    max = 0
    sum = 0
    for key in k:
        l = len(key)
        sum += l
        if l < min:
            min = l
        if l > max:
            max = l
    avg = sum / len(k)

    print(f"Avg time: {(total / 20.0) / len(corpus)}, total elements: {len(corpus)}, unique elements: {len(k)}, collisions: {len(k) / len(v)}, collisions % 512: {len(k) / len(v512)}, keys min: {min}, avg: {avg}, max: {max}")


def main():
    corpus1 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque orci urna, pretium et porta ac, porttitor sit amet sem. Fusce sagittis lorem neque, vitae sollicitudin elit suscipit et. In interdum convallis nisl in ornare. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aliquam erat volutpat. Morbi mollis iaculis lectus ac tincidunt. Fusce nisi lacus, semper eu dignissim et, malesuada non mi. Sed euismod urna vel elit faucibus, eu bibendum ante fringilla. Curabitur tempus in turpis at mattis. Aliquam erat volutpat. Donec maximus elementum felis, sit amet dignissim augue tincidunt blandit. Aliquam fermentum, est eu mollis.".split(" ")
    corpus2 = "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains. But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains.But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection:".split(" ")
    corpus3 = "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions! A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions! A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions!A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls".split(" ")
    corpus4 = "Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое. Лежа на панцирнотвердой спине, он видел, стоило ему приподнять голову, свой коричневый, выпуклый, разделенный дугообразными чешуйками живот, на верхушке которого еле держалось готовое вот-вот окончательно сползти одеяло. Его многочисленные, убого тонкие по сравнению с остальным телом ножки беспомощно копошились у него перед глазами. «Что со мной случилось?» – подумал он. Это не было сном. Его комната, настоящая, разве что слишком маленькая, но обычная комната, мирно покоилась в своих четырех хорошо знакомых стенах. Над столом, где были разложены распакованные образцы сукон – Замза был коммивояжером, – висел портрет, который он недавно вырезал из иллюстрированного журнала и вставил в красивую золоченую рамку. На портрете была изображена дама в меховой шляпе и боа, она сидела очень прямо и протягивала зрителю тяжелую меховую муфту, в которой целиком исчезала ее рука. Затем взгляд Грегора устремился в окно, и пасмурная погода – слышно было, как по жести подоконника стучат капли дождя – привела его и вовсе в грустное настроение. «Хорошо бы еще немного поспать и забыть всю эту чепуху», – подумал он, но это было совершенно неосуществимо, он привык спать на правом боку, а в теперешнем своем состоянии он никак не мог принять этого положения. С какой бы силой ни поворачивался он на правый бок, он неизменно сваливался опять на спину. Закрыв глаза, чтобы не видеть своих барахтающихся ног, он проделал это добрую сотню раз и отказался от этих попыток только тогда, когда почувствовал какую-то неведомую дотоле, тупую и слабую боль в боку. «Ах ты, господи, – подумал он, – какую я выбрал хлопотную профессию! Изо дня в день в разъездах. Деловых волнений куда больше, чем на месте, в торговом доме, а кроме того, изволь терпеть тяготы дороги, думай о расписании поездов, мирись с плохим, нерегулярным питанием, завязывай со все новыми и новыми людьми недолгие, никогда не бывающие сердечными отношения. Черт бы побрал все это!» Он почувствовал вверху живота легкий зуд; медленно подвинулся на спине к прутьям кровати, чтобы удобнее было поднять голову; нашел зудевшее место, сплошь покрытое, как оказалось, белыми непонятными точечками; хотел было ощупать это место одной из ножек, но сразу отдернул ее, ибо даже простое прикосновение вызвало у него, Грегора, озноб. Он соскользнул в прежнее свое положение. «От этого раннего вставания, – подумал он, – можно совсем обезуметь. Человек должен высыпаться. Другие коммивояжеры живут, как одалиски. Когда я, например, среди дня возвращаюсь в гостиницу, чтобы переписать полученные заказы, эти господа только завтракают. А осмелься я вести себя так, мои хозяин выгнал бы меня сразу. Кто знает, впрочем, может быть, это было бы даже очень хорошо для меня. Если бы я не сдерживался ради родителей, я бы давно заявил об уходе, я бы подошел к своему хозяину и выложил ему все, что о нем думаю. Он бы так и свалился с конторки! Странная у него манера – садиться на конторку и с ее высоты разговаривать со служащим, который вдобавок вынужден подойти вплотную к конторке из-за того, что хозяин туг на ухо. Однако надежда еще не совсем потеряна: как только я накоплю денег, чтобы выплатить долг моих родителей – на это уйдет еще лет пять-шесть, – я так и поступлю. Тут-то мы и распрощаемся раз и навсегда. А пока что надо подниматься, мой поезд отходит в пять». И он взглянул на будильник, который тикал на сундуке. «Боже правый!» – подумал он. Было половина седьмого, и стрелки спокойно двигались дальше, было даже больше половины, без малого уже три четверти. Неужели будильник не звонил? С кровати было видно, что он поставлен правильно, на четыре часа; и он, несомненно, звонил. Но как можно было спокойно спать под этот сотрясающий мебель трезвон? Ну, спал-то он неспокойно, но, видимо, крепко. Однако что делать теперь? Следующий поезд уходит в семь часов; чтобы поспеть на него, он должен отчаянно торопиться, а набор образцов еще не упакован, да и сам он отнюдь не чувствует себя свежим и легким на подъем. И даже поспей он на поезд, хозяйского разноса ему все равно не избежать – ведь рассыльный торгового дома дежурил у пятичасового поезда и давно доложил о его, Грегора, опоздании. Рассыльный, человек бесхарактерный и неумный, был ставленником хозяина. А что, если сказаться больным? Но это было бы крайне неприятно и показалось бы подозрительным, ибо за пятилетнюю свою службу Грегор ни разу еще не болел. Хозяин, конечно, привел бы врача больничной кассы и стал попрекать родителей сыном-лентяем, отводя любые возражения ссылкой на этого врача, по мнению которого все люди на свете совершенно здоровы и только не любят работать. И разве в данном случае он был бы так уж неправ? Если не считать сонливости, действительно странной после такого долгого сна, Грегор и в самом деле чувствовал себя превосходно и был даже чертовски голоден.Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое. Лежа на панцирнотвердой спине, он видел, стоило ему приподнять голову, свой коричневый, выпуклый, разделенный дугообразными чешуйками живот, на верхушке которого еле держалось готовое вот-вот окончательно сползти одеяло. Его многочисленные, убого тонкие по сравнению с остальным телом ножки беспомощно копошились у него перед глазами. «Что со мной случилось?» – подумал он. Это не было сном. Его комната, настоящая, разве что слишком маленькая, но обычная комната, мирно покоилась в своих четырех хорошо знакомых стенах. Над столом, где были разложены распакованные образцы сукон – Замза был коммивояжером, – висел портрет, который он недавно вырезал из иллюстрированного журнала и вставил в красивую золоченую рамку. На портрете была изображена дама в меховой шляпе и боа, она сидела очень прямо и протягивала зрителю тяжелую меховую муфту, в которой целиком исчезала ее рука. Затем взгляд Грегора устремился в окно, и пасмурная погода – слышно было, как по жести подоконника стучат капли дождя – привела его и вовсе в грустное настроение. «Хорошо бы еще немного поспать и забыть всю эту чепуху», – подумал он, но это было совершенно неосуществимо, он привык спать на правом боку, а в теперешнем своем состоянии он никак не мог принять этого положения. С какой бы силой ни поворачивался он на правый бок, он неизменно сваливался опять на спину.".split(" ")
    corpus5 = "Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch. Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch. Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch.Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen".split(" ")
    corpus6 = "米くを舵4物委らご氏松ハナテフ月関ソ時平ふいの博情れじフ牟万い元56園フメヤオ試図ロツヤ未備王こと傷喫羅踊んゆし。栃ユヱオ書著作ユソツロ英祉業ア大課ご権質フべ空8午キ切軟づン著郎そゃす格町採ヱオマコ処8付国ムハチア究表でなだ際無ロミヱ地兵ぴげ庭体すク発抜爆位や。楽富むゆず盛航カナセ携代ハ本高きた員59今骸ンラえぜ城解イケ穴訴ぽぎ属住ヤケトヌ抱点ト広注厚でて。 国リ出難セユメ軍手ヘカウ画形サヲシ猛85用ヲキミ心死よしと身処ケヨミオ教主ーぽ事業んく字国たさょ図能シミスヤ社8板ル岡世58次戒知院んれり。市メ誘根カ数問禁竹ゃれえみ給辺のでみき今二ぎさ裕止過こクすと無32郎所ラた生展ヌヘス成度慣葬勇厘ばてか。室ゃ下携疲ム色権がぽりっ銃週ノオ姫千テム健蔵い研手ッ放容ル告属め旅側26企サノヨ宅都福ぞ通待ちぴね種脳イど労希望義通むン。 罰しい続負せ著低たル異師ユハワ東添質コチ転集ルヤ雇聴約ヒ前統らた情厳ゆさでや真胸や有披暑棚豆ゆぼたけ。盛ワセロナ情競クるっわ講3音ずをせ少地めしぜょ手63明視れに判企ヒヌエソ求総58特本ね井比ユラキ禁頭馬るゅリす能率率かがさわ。葉サソ医郡ヱヘソ労帰ナケスミ救写ワヘ株審ネヒニミ安逮イ人画ラ涯車はラ極騒りなド件5級ンかふー劇41著ぱぐ凱討だ文世ぶづどま界善魅マ渓経競融れがや。 連ーぜらご模分ッ視外ばフく運発群ほぼづ育越一ほごクけ案募ヲイソ治会イせフ製君ぜた漢村1変リヒ構5際ツ御文ヲ臭入さドぼ代書ハケ引技ろみれ回観注倉徹ぱ。論ラづ海要サ情座ゃり齢宣ラモエ芸化エマホ覧催回ら戦69本外ト葬岳な政画か連針ぴリフず。約ル闘辺ぽ経2応掲ホサアラ塾小コラ画決クノオ上室レヌヱ勝逮ぜるえむ責豊チノ明意ひけ訟6碁草メタチエ財午召喝塊む。 決めでわ名金つけレわ続人県約ぽぼす尾腹ユサ戦載リシ護賀レモフツ重涯ニ治者むんっみ職更カタチレ提話2何ワ責東まけげふ能政ヌ供禁がびてわ提改倶れめ。読み担後ぽ安加ぎ論鹿ツ統最お気麻月つじもあ竜思いろめ判必満理トコ文連ムイウハ寄串ざほびー。文ゆこっ向27年メイ便能ノセヲ待1王スねたゆ伝派んね点過カト治読よにきべ使人スシ都言え阻8割べづえみ注引敷的岳犠眠どそ。 学用イだ医客開ロ供界もぞだ実隆モイヌ務坂ナコヲ権野ろづ初場ぱ低会づぱじ新倒コ化政レ止奮浸猪ッわえづ。形いやリ要帰ほまむだ業領スル必打さ島14巻リ集日ネヘホタ面幅ち写上そぴ円図ムタコモ報使イわざと会催ヤヲ康証をドぶレ盤岡ホハツ作29管しをめ公問懐蓄っさ。来ゆぼあぱ投秋シ語右ぐ身靖かば辛握捕家記ヘワ神岐囲づ毘観メテクツ政73夕罪57需93誌飲査仁さ。 変レめ束球よんま会特ヱコ聞重だ史純ーどる件32浦レぴよゃ上強ネラリロ査従セユヤ専棋光レ作表ひぶ予正ぜーな誉確フス函6報円ス進治ね能営済否雄でわょ。42生型ば着続ア短実ぎおめび前環闘ラヤヲル診均っとにの声公トヱテマ整試椅情久妊舌頃ざとっく。品キチトテ阿国ラら受87世ヲフセリ川86個ーょぼげ危子ヘレカメ無会ぱかへ事通んかて電条ロツ徴商ぶぞそを居暑メ害広せもがり禁応レミヲ応響割壮憶はぱ。 千れンが織財メニ況界ネトレミ学豊フオホシ近月レたやご的罪ょな菱技ちる警栗エセ提89林危氷48参ア説森クキヒヱ薬社ホコエリ負和ルび紀下ケミイ掲歳特ごず扱底ク護木連ちクを各形ばすか。変ぱなれ町7融ヌ街準以タユヘム質裕ぶで遺語俊ぎずょ事金文キ写多山ーゆに歩帯すで会世クぜよ論写ヲ達71林危氷5間続ぎぜび高怠す。 係8青け応著ミ戦条ナヘネカ思79未ぎ算伊をゃ泉人ーづ需説っ畑鹿27軽ラソツ権2促千護ルロナカ開国ケ暴嶋ご池表だ。佐フナ訪麻はてせば勝効をあ医戦画とさわぴ者両すいあ並来んば載食ぴ件友頂業へえぞ魚祝ネラ聞率スコリケ始全ンこび夫出ドふ今布うぎふゅ実克即哉循やしんな。 暮す備54依紀てッん末刊と柔称むてス無府ケイ変壌をぱ汁連フマス海世ヌ中負知問ナヘケ純推ひ読着ヒ言若私軽れ。掲けフむ王本オコ線人をっさ必和断セソヲハ図芸ちかな防長りぶは投新意相ツ並5余セ職岳ぞ端古空援そ。森ヨエチ題5東っ自兄ち暴5近鹿横ト的京ハ安氷ナキ深際ぎ並節くスむの権工ほルせ京49効タムチ処三ぞぴラ済国ずっ文経ヘトミ水分準そが。".split(" ")
    corpus7 = "AbortMultipartUpload CompleteMultipartUpload CopyObject CreateBucket CreateMultipartUpload DeleteBucket DeleteBucketAnalyticsConfiguration DeleteBucketCors DeleteBucketEncryption DeleteBucketIntelligentTieringConfiguration DeleteBucketInventoryConfiguration DeleteBucketLifecycle DeleteBucketMetricsConfiguration DeleteBucketOwnershipControls DeleteBucketPolicy DeleteBucketReplication DeleteBucketTagging DeleteBucketWebsite DeleteObject DeleteObjects DeleteObjectTagging DeletePublicAccessBlock GetBucketAccelerateConfiguration GetBucketAcl GetBucketAnalyticsConfiguration GetBucketCors GetBucketEncryption GetBucketIntelligentTieringConfiguration GetBucketInventoryConfiguration GetBucketLifecycle GetBucketLifecycleConfiguration GetBucketLocation GetBucketLogging GetBucketMetricsConfiguration GetBucketNotification GetBucketNotificationConfiguration GetBucketOwnershipControls GetBucketPolicy GetBucketPolicyStatus GetBucketReplication GetBucketRequestPayment GetBucketTagging GetBucketVersioning GetBucketWebsite GetObject GetObjectAcl GetObjectAttributes GetObjectLegalHold GetObjectLockConfiguration GetObjectRetention GetObjectTagging GetObjectTorrent GetPublicAccessBlock HeadBucket HeadObject ListBucketAnalyticsConfigurations ListBucketIntelligentTieringConfigurations ListBucketInventoryConfigurations ListBucketMetricsConfigurations ListBuckets ListMultipartUploads ListObjects ListObjectsV2 ListObjectVersions ListParts PutBucketAccelerateConfiguration PutBucketAcl PutBucketAnalyticsConfiguration PutBucketCors PutBucketEncryption PutBucketIntelligentTieringConfiguration PutBucketInventoryConfiguration PutBucketLifecycle PutBucketLifecycleConfiguration PutBucketLogging PutBucketMetricsConfiguration PutBucketNotification PutBucketNotificationConfiguration PutBucketOwnershipControls PutBucketPolicy PutBucketReplication PutBucketRequestPayment PutBucketTagging PutBucketVersioning PutBucketWebsite PutObject PutObjectAcl PutObjectLegalHold PutObjectLockConfiguration PutObjectRetention PutObjectTagging PutPublicAccessBlock RestoreObject SelectObjectContent UploadPart UploadPartCopy WriteGetObjectResponse CreateAccessPoint CreateAccessPointForObjectLambda CreateBucket CreateJob CreateMultiRegionAccessPoint DeleteAccessPoint DeleteAccessPointForObjectLambda DeleteAccessPointPolicy DeleteAccessPointPolicyForObjectLambda DeleteBucket DeleteBucketLifecycleConfiguration DeleteBucketPolicy DeleteBucketReplication DeleteBucketTagging DeleteJobTagging DeleteMultiRegionAccessPoint DeletePublicAccessBlock DeleteStorageLensConfiguration DeleteStorageLensConfigurationTagging DescribeJob DescribeMultiRegionAccessPointOperation GetAccessPoint GetAccessPointConfigurationForObjectLambda GetAccessPointForObjectLambda GetAccessPointPolicy GetAccessPointPolicyForObjectLambda GetAccessPointPolicyStatus GetAccessPointPolicyStatusForObjectLambda GetBucket GetBucketLifecycleConfiguration GetBucketPolicy GetBucketReplication GetBucketTagging GetBucketVersioning GetJobTagging GetMultiRegionAccessPoint GetMultiRegionAccessPointPolicy GetMultiRegionAccessPointPolicyStatus GetMultiRegionAccessPointRoutes GetPublicAccessBlock GetStorageLensConfiguration GetStorageLensConfigurationTagging ListAccessPoints ListAccessPointsForObjectLambda ListJobs ListMultiRegionAccessPoints ListRegionalBuckets ListStorageLensConfigurations PutAccessPointConfigurationForObjectLambda PutAccessPointPolicy PutAccessPointPolicyForObjectLambda PutBucketLifecycleConfiguration PutBucketPolicy PutBucketReplication PutBucketTagging PutBucketVersioning PutJobTagging PutMultiRegionAccessPointPolicy PutPublicAccessBlock PutStorageLensConfiguration PutStorageLensConfigurationTagging SubmitMultiRegionAccessPointRoutes UpdateJobPriority UpdateJobStatus".split(" ")
    corps = [corpus1, corpus2, corpus3, corpus4, corpus5, corpus6, corpus7]
    for corpus in corps:
        benchmark(corpus)
    

if __name__ == "__main__":
    main()

--- benchmark_other_languages/python/hash_functions/benchmark_dict.py ---

from pathlib import Path
from time import time_ns

def main():
    corpus1 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque orci urna, pretium et porta ac, porttitor sit amet sem. Fusce sagittis lorem neque, vitae sollicitudin elit suscipit et. In interdum convallis nisl in ornare. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aliquam erat volutpat. Morbi mollis iaculis lectus ac tincidunt. Fusce nisi lacus, semper eu dignissim et, malesuada non mi. Sed euismod urna vel elit faucibus, eu bibendum ante fringilla. Curabitur tempus in turpis at mattis. Aliquam erat volutpat. Donec maximus elementum felis, sit amet dignissim augue tincidunt blandit. Aliquam fermentum, est eu mollis.".split(" ")
    corpus2 = "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains. But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains.But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection:".split(" ")
    corpus3 = "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions! A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions! A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions!A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls".split(" ")
    corpus4 = "Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое. Лежа на панцирнотвердой спине, он видел, стоило ему приподнять голову, свой коричневый, выпуклый, разделенный дугообразными чешуйками живот, на верхушке которого еле держалось готовое вот-вот окончательно сползти одеяло. Его многочисленные, убого тонкие по сравнению с остальным телом ножки беспомощно копошились у него перед глазами. «Что со мной случилось?» – подумал он. Это не было сном. Его комната, настоящая, разве что слишком маленькая, но обычная комната, мирно покоилась в своих четырех хорошо знакомых стенах. Над столом, где были разложены распакованные образцы сукон – Замза был коммивояжером, – висел портрет, который он недавно вырезал из иллюстрированного журнала и вставил в красивую золоченую рамку. На портрете была изображена дама в меховой шляпе и боа, она сидела очень прямо и протягивала зрителю тяжелую меховую муфту, в которой целиком исчезала ее рука. Затем взгляд Грегора устремился в окно, и пасмурная погода – слышно было, как по жести подоконника стучат капли дождя – привела его и вовсе в грустное настроение. «Хорошо бы еще немного поспать и забыть всю эту чепуху», – подумал он, но это было совершенно неосуществимо, он привык спать на правом боку, а в теперешнем своем состоянии он никак не мог принять этого положения. С какой бы силой ни поворачивался он на правый бок, он неизменно сваливался опять на спину. Закрыв глаза, чтобы не видеть своих барахтающихся ног, он проделал это добрую сотню раз и отказался от этих попыток только тогда, когда почувствовал какую-то неведомую дотоле, тупую и слабую боль в боку. «Ах ты, господи, – подумал он, – какую я выбрал хлопотную профессию! Изо дня в день в разъездах. Деловых волнений куда больше, чем на месте, в торговом доме, а кроме того, изволь терпеть тяготы дороги, думай о расписании поездов, мирись с плохим, нерегулярным питанием, завязывай со все новыми и новыми людьми недолгие, никогда не бывающие сердечными отношения. Черт бы побрал все это!» Он почувствовал вверху живота легкий зуд; медленно подвинулся на спине к прутьям кровати, чтобы удобнее было поднять голову; нашел зудевшее место, сплошь покрытое, как оказалось, белыми непонятными точечками; хотел было ощупать это место одной из ножек, но сразу отдернул ее, ибо даже простое прикосновение вызвало у него, Грегора, озноб. Он соскользнул в прежнее свое положение. «От этого раннего вставания, – подумал он, – можно совсем обезуметь. Человек должен высыпаться. Другие коммивояжеры живут, как одалиски. Когда я, например, среди дня возвращаюсь в гостиницу, чтобы переписать полученные заказы, эти господа только завтракают. А осмелься я вести себя так, мои хозяин выгнал бы меня сразу. Кто знает, впрочем, может быть, это было бы даже очень хорошо для меня. Если бы я не сдерживался ради родителей, я бы давно заявил об уходе, я бы подошел к своему хозяину и выложил ему все, что о нем думаю. Он бы так и свалился с конторки! Странная у него манера – садиться на конторку и с ее высоты разговаривать со служащим, который вдобавок вынужден подойти вплотную к конторке из-за того, что хозяин туг на ухо. Однако надежда еще не совсем потеряна: как только я накоплю денег, чтобы выплатить долг моих родителей – на это уйдет еще лет пять-шесть, – я так и поступлю. Тут-то мы и распрощаемся раз и навсегда. А пока что надо подниматься, мой поезд отходит в пять». И он взглянул на будильник, который тикал на сундуке. «Боже правый!» – подумал он. Было половина седьмого, и стрелки спокойно двигались дальше, было даже больше половины, без малого уже три четверти. Неужели будильник не звонил? С кровати было видно, что он поставлен правильно, на четыре часа; и он, несомненно, звонил. Но как можно было спокойно спать под этот сотрясающий мебель трезвон? Ну, спал-то он неспокойно, но, видимо, крепко. Однако что делать теперь? Следующий поезд уходит в семь часов; чтобы поспеть на него, он должен отчаянно торопиться, а набор образцов еще не упакован, да и сам он отнюдь не чувствует себя свежим и легким на подъем. И даже поспей он на поезд, хозяйского разноса ему все равно не избежать – ведь рассыльный торгового дома дежурил у пятичасового поезда и давно доложил о его, Грегора, опоздании. Рассыльный, человек бесхарактерный и неумный, был ставленником хозяина. А что, если сказаться больным? Но это было бы крайне неприятно и показалось бы подозрительным, ибо за пятилетнюю свою службу Грегор ни разу еще не болел. Хозяин, конечно, привел бы врача больничной кассы и стал попрекать родителей сыном-лентяем, отводя любые возражения ссылкой на этого врача, по мнению которого все люди на свете совершенно здоровы и только не любят работать. И разве в данном случае он был бы так уж неправ? Если не считать сонливости, действительно странной после такого долгого сна, Грегор и в самом деле чувствовал себя превосходно и был даже чертовски голоден.Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое. Лежа на панцирнотвердой спине, он видел, стоило ему приподнять голову, свой коричневый, выпуклый, разделенный дугообразными чешуйками живот, на верхушке которого еле держалось готовое вот-вот окончательно сползти одеяло. Его многочисленные, убого тонкие по сравнению с остальным телом ножки беспомощно копошились у него перед глазами. «Что со мной случилось?» – подумал он. Это не было сном. Его комната, настоящая, разве что слишком маленькая, но обычная комната, мирно покоилась в своих четырех хорошо знакомых стенах. Над столом, где были разложены распакованные образцы сукон – Замза был коммивояжером, – висел портрет, который он недавно вырезал из иллюстрированного журнала и вставил в красивую золоченую рамку. На портрете была изображена дама в меховой шляпе и боа, она сидела очень прямо и протягивала зрителю тяжелую меховую муфту, в которой целиком исчезала ее рука. Затем взгляд Грегора устремился в окно, и пасмурная погода – слышно было, как по жести подоконника стучат капли дождя – привела его и вовсе в грустное настроение. «Хорошо бы еще немного поспать и забыть всю эту чепуху», – подумал он, но это было совершенно неосуществимо, он привык спать на правом боку, а в теперешнем своем состоянии он никак не мог принять этого положения. С какой бы силой ни поворачивался он на правый бок, он неизменно сваливался опять на спину.".split(" ")
    corpus5 = "Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch. Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch. Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch.Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen".split(" ")
    corpus6 = "米くを舵4物委らご氏松ハナテフ月関ソ時平ふいの博情れじフ牟万い元56園フメヤオ試図ロツヤ未備王こと傷喫羅踊んゆし。栃ユヱオ書著作ユソツロ英祉業ア大課ご権質フべ空8午キ切軟づン著郎そゃす格町採ヱオマコ処8付国ムハチア究表でなだ際無ロミヱ地兵ぴげ庭体すク発抜爆位や。楽富むゆず盛航カナセ携代ハ本高きた員59今骸ンラえぜ城解イケ穴訴ぽぎ属住ヤケトヌ抱点ト広注厚でて。 国リ出難セユメ軍手ヘカウ画形サヲシ猛85用ヲキミ心死よしと身処ケヨミオ教主ーぽ事業んく字国たさょ図能シミスヤ社8板ル岡世58次戒知院んれり。市メ誘根カ数問禁竹ゃれえみ給辺のでみき今二ぎさ裕止過こクすと無32郎所ラた生展ヌヘス成度慣葬勇厘ばてか。室ゃ下携疲ム色権がぽりっ銃週ノオ姫千テム健蔵い研手ッ放容ル告属め旅側26企サノヨ宅都福ぞ通待ちぴね種脳イど労希望義通むン。 罰しい続負せ著低たル異師ユハワ東添質コチ転集ルヤ雇聴約ヒ前統らた情厳ゆさでや真胸や有披暑棚豆ゆぼたけ。盛ワセロナ情競クるっわ講3音ずをせ少地めしぜょ手63明視れに判企ヒヌエソ求総58特本ね井比ユラキ禁頭馬るゅリす能率率かがさわ。葉サソ医郡ヱヘソ労帰ナケスミ救写ワヘ株審ネヒニミ安逮イ人画ラ涯車はラ極騒りなド件5級ンかふー劇41著ぱぐ凱討だ文世ぶづどま界善魅マ渓経競融れがや。 連ーぜらご模分ッ視外ばフく運発群ほぼづ育越一ほごクけ案募ヲイソ治会イせフ製君ぜた漢村1変リヒ構5際ツ御文ヲ臭入さドぼ代書ハケ引技ろみれ回観注倉徹ぱ。論ラづ海要サ情座ゃり齢宣ラモエ芸化エマホ覧催回ら戦69本外ト葬岳な政画か連針ぴリフず。約ル闘辺ぽ経2応掲ホサアラ塾小コラ画決クノオ上室レヌヱ勝逮ぜるえむ責豊チノ明意ひけ訟6碁草メタチエ財午召喝塊む。 決めでわ名金つけレわ続人県約ぽぼす尾腹ユサ戦載リシ護賀レモフツ重涯ニ治者むんっみ職更カタチレ提話2何ワ責東まけげふ能政ヌ供禁がびてわ提改倶れめ。読み担後ぽ安加ぎ論鹿ツ統最お気麻月つじもあ竜思いろめ判必満理トコ文連ムイウハ寄串ざほびー。文ゆこっ向27年メイ便能ノセヲ待1王スねたゆ伝派んね点過カト治読よにきべ使人スシ都言え阻8割べづえみ注引敷的岳犠眠どそ。 学用イだ医客開ロ供界もぞだ実隆モイヌ務坂ナコヲ権野ろづ初場ぱ低会づぱじ新倒コ化政レ止奮浸猪ッわえづ。形いやリ要帰ほまむだ業領スル必打さ島14巻リ集日ネヘホタ面幅ち写上そぴ円図ムタコモ報使イわざと会催ヤヲ康証をドぶレ盤岡ホハツ作29管しをめ公問懐蓄っさ。来ゆぼあぱ投秋シ語右ぐ身靖かば辛握捕家記ヘワ神岐囲づ毘観メテクツ政73夕罪57需93誌飲査仁さ。 変レめ束球よんま会特ヱコ聞重だ史純ーどる件32浦レぴよゃ上強ネラリロ査従セユヤ専棋光レ作表ひぶ予正ぜーな誉確フス函6報円ス進治ね能営済否雄でわょ。42生型ば着続ア短実ぎおめび前環闘ラヤヲル診均っとにの声公トヱテマ整試椅情久妊舌頃ざとっく。品キチトテ阿国ラら受87世ヲフセリ川86個ーょぼげ危子ヘレカメ無会ぱかへ事通んかて電条ロツ徴商ぶぞそを居暑メ害広せもがり禁応レミヲ応響割壮憶はぱ。 千れンが織財メニ況界ネトレミ学豊フオホシ近月レたやご的罪ょな菱技ちる警栗エセ提89林危氷48参ア説森クキヒヱ薬社ホコエリ負和ルび紀下ケミイ掲歳特ごず扱底ク護木連ちクを各形ばすか。変ぱなれ町7融ヌ街準以タユヘム質裕ぶで遺語俊ぎずょ事金文キ写多山ーゆに歩帯すで会世クぜよ論写ヲ達71林危氷5間続ぎぜび高怠す。 係8青け応著ミ戦条ナヘネカ思79未ぎ算伊をゃ泉人ーづ需説っ畑鹿27軽ラソツ権2促千護ルロナカ開国ケ暴嶋ご池表だ。佐フナ訪麻はてせば勝効をあ医戦画とさわぴ者両すいあ並来んば載食ぴ件友頂業へえぞ魚祝ネラ聞率スコリケ始全ンこび夫出ドふ今布うぎふゅ実克即哉循やしんな。 暮す備54依紀てッん末刊と柔称むてス無府ケイ変壌をぱ汁連フマス海世ヌ中負知問ナヘケ純推ひ読着ヒ言若私軽れ。掲けフむ王本オコ線人をっさ必和断セソヲハ図芸ちかな防長りぶは投新意相ツ並5余セ職岳ぞ端古空援そ。森ヨエチ題5東っ自兄ち暴5近鹿横ト的京ハ安氷ナキ深際ぎ並節くスむの権工ほルせ京49効タムチ処三ぞぴラ済国ずっ文経ヘトミ水分準そが。".split(" ")
    corpus7 = "AbortMultipartUpload CompleteMultipartUpload CopyObject CreateBucket CreateMultipartUpload DeleteBucket DeleteBucketAnalyticsConfiguration DeleteBucketCors DeleteBucketEncryption DeleteBucketIntelligentTieringConfiguration DeleteBucketInventoryConfiguration DeleteBucketLifecycle DeleteBucketMetricsConfiguration DeleteBucketOwnershipControls DeleteBucketPolicy DeleteBucketReplication DeleteBucketTagging DeleteBucketWebsite DeleteObject DeleteObjects DeleteObjectTagging DeletePublicAccessBlock GetBucketAccelerateConfiguration GetBucketAcl GetBucketAnalyticsConfiguration GetBucketCors GetBucketEncryption GetBucketIntelligentTieringConfiguration GetBucketInventoryConfiguration GetBucketLifecycle GetBucketLifecycleConfiguration GetBucketLocation GetBucketLogging GetBucketMetricsConfiguration GetBucketNotification GetBucketNotificationConfiguration GetBucketOwnershipControls GetBucketPolicy GetBucketPolicyStatus GetBucketReplication GetBucketRequestPayment GetBucketTagging GetBucketVersioning GetBucketWebsite GetObject GetObjectAcl GetObjectAttributes GetObjectLegalHold GetObjectLockConfiguration GetObjectRetention GetObjectTagging GetObjectTorrent GetPublicAccessBlock HeadBucket HeadObject ListBucketAnalyticsConfigurations ListBucketIntelligentTieringConfigurations ListBucketInventoryConfigurations ListBucketMetricsConfigurations ListBuckets ListMultipartUploads ListObjects ListObjectsV2 ListObjectVersions ListParts PutBucketAccelerateConfiguration PutBucketAcl PutBucketAnalyticsConfiguration PutBucketCors PutBucketEncryption PutBucketIntelligentTieringConfiguration PutBucketInventoryConfiguration PutBucketLifecycle PutBucketLifecycleConfiguration PutBucketLogging PutBucketMetricsConfiguration PutBucketNotification PutBucketNotificationConfiguration PutBucketOwnershipControls PutBucketPolicy PutBucketReplication PutBucketRequestPayment PutBucketTagging PutBucketVersioning PutBucketWebsite PutObject PutObjectAcl PutObjectLegalHold PutObjectLockConfiguration PutObjectRetention PutObjectTagging PutPublicAccessBlock RestoreObject SelectObjectContent UploadPart UploadPartCopy WriteGetObjectResponse CreateAccessPoint CreateAccessPointForObjectLambda CreateBucket CreateJob CreateMultiRegionAccessPoint DeleteAccessPoint DeleteAccessPointForObjectLambda DeleteAccessPointPolicy DeleteAccessPointPolicyForObjectLambda DeleteBucket DeleteBucketLifecycleConfiguration DeleteBucketPolicy DeleteBucketReplication DeleteBucketTagging DeleteJobTagging DeleteMultiRegionAccessPoint DeletePublicAccessBlock DeleteStorageLensConfiguration DeleteStorageLensConfigurationTagging DescribeJob DescribeMultiRegionAccessPointOperation GetAccessPoint GetAccessPointConfigurationForObjectLambda GetAccessPointForObjectLambda GetAccessPointPolicy GetAccessPointPolicyForObjectLambda GetAccessPointPolicyStatus GetAccessPointPolicyStatusForObjectLambda GetBucket GetBucketLifecycleConfiguration GetBucketPolicy GetBucketReplication GetBucketTagging GetBucketVersioning GetJobTagging GetMultiRegionAccessPoint GetMultiRegionAccessPointPolicy GetMultiRegionAccessPointPolicyStatus GetMultiRegionAccessPointRoutes GetPublicAccessBlock GetStorageLensConfiguration GetStorageLensConfigurationTagging ListAccessPoints ListAccessPointsForObjectLambda ListJobs ListMultiRegionAccessPoints ListRegionalBuckets ListStorageLensConfigurations PutAccessPointConfigurationForObjectLambda PutAccessPointPolicy PutAccessPointPolicyForObjectLambda PutBucketLifecycleConfiguration PutBucketPolicy PutBucketReplication PutBucketTagging PutBucketVersioning PutJobTagging PutMultiRegionAccessPointPolicy PutPublicAccessBlock PutStorageLensConfiguration PutStorageLensConfigurationTagging SubmitMultiRegionAccessPointRoutes UpdateJobPriority UpdateJobStatus".split(" ")
    corpus8 = Path("/usr/share/dict/words").read_text().splitlines()
    all = [corpus1, corpus2, corpus3, corpus4, corpus5, corpus6, corpus7, corpus8]
    for j, corpus in enumerate(all):
        total_put = 0
        total_get = 0
        for _ in range(10):
            sum = 0
            d = {}

            for i, c in enumerate(corpus):
                tik = time_ns()
                d[c] = i
                tok = time_ns()
                total_put += tok - tik

            for c in corpus:
                tik = time_ns()
                a = d[c]
                tok = time_ns()
                total_get += tok - tik
                sum += a
        print(f"Corpus {j + 1}")
        print(f"Avg time put: {(total_put / 10.0) / len(corpus)}")
        print(f"Avg time get: {(total_get / 10.0) / len(corpus)}")
        print(sum)

if __name__ == "__main__":
    main()


--- benchmark_other_languages/python/hash_functions/md5_benchmark.py ---

import hashlib
import time

if __name__ == "__main__":
    file = open("/usr/share/dict/words", "r")
    content = file.read().encode()
    tik = time.time_ns()
    result = hashlib.md5(content)
    tok = time.time_ns()
    print(result.hexdigest())
    print(f"In: {tok - tik}")

    tik = time.time_ns()
    result = hashlib.sha256(content)
    tok = time.time_ns()
    print(result.hexdigest())
    print(f"In: {tok - tik}")



--- benchmark_other_languages/rust/hash_functions/Cargo.lock ---

# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3

[[package]]
name = "ahash"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
dependencies = [
 "cfg-if",
 "getrandom",
 "once_cell",
 "version_check",
 "zerocopy",
]

[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"

[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"

[[package]]
name = "fxhash"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
dependencies = [
 "byteorder",
]

[[package]]
name = "getrandom"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
dependencies = [
 "cfg-if",
 "libc",
 "wasi",
]

[[package]]
name = "hash_functions"
version = "0.1.0"
dependencies = [
 "ahash",
 "fxhash",
 "md5",
 "rand",
 "wyhash2",
]

[[package]]
name = "libc"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"

[[package]]
name = "md5"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"

[[package]]
name = "no-std-compat"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"

[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"

[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"

[[package]]
name = "proc-macro2"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"
dependencies = [
 "unicode-ident",
]

[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
 "proc-macro2",
]

[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
 "libc",
 "rand_chacha",
 "rand_core",
]

[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
 "ppv-lite86",
 "rand_core",
]

[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
 "getrandom",
]

[[package]]
name = "syn"
version = "2.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8"
dependencies = [
 "proc-macro2",
 "quote",
 "unicode-ident",
]

[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"

[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"

[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"

[[package]]
name = "wyhash2"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9433c7c86e328a8197038c9fc31f6e5c81f9c7bdc087d86e266680236af1af1b"
dependencies = [
 "no-std-compat",
]

[[package]]
name = "zerocopy"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
dependencies = [
 "zerocopy-derive",
]

[[package]]
name = "zerocopy-derive"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]


--- benchmark_other_languages/rust/hash_functions/Cargo.toml ---

[package]
name = "hash_functions"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rand = "0.8.5"
fxhash = "0.2.1"
ahash = "0.8.6"
wyhash2 = "0.2.1"
md5 = "0.7.0"

[profile.dev]
opt-level = 3

--- benchmark_other_languages/rust/hash_functions/src/main.rs ---

use std::{collections::{HashSet, hash_map::DefaultHasher}, vec, fs};
use std::hash::{Hash, Hasher};
use ahash::AHasher;
use fxhash::FxHasher64;
use wyhash2::{WyHash, wyhash_single};
use std::time::Instant;


fn test_hashing<T: Hasher + Default>(corpus: &Vec<&str>, name: &str) {
    let mut total = 0;
    let mut k = HashSet::new();
    let mut v = HashSet::new();
    let mut v512: HashSet<u64> = HashSet::new();
    for _ in 0..20 {
        v.clear();
        v512.clear();
        corpus.iter().for_each(|key| {
            let mut s = T::default();
            k.insert(key);
            let tik = Instant::now();
            key.hash(&mut s);
            let hash = s.finish();
            let tok = Instant::now();
            total += (tok - tik).as_nanos();
            v.insert(hash);
            v512.insert(hash % 512);
        });
    }
    let mut min = 1000000;
    let mut max = 0;
    let mut sum = 0;
    for key in k.iter() {
        let l = key.len();
        sum += l;
        if l > max {
            max = l;
        }
        if l < min {
            min = l;
        }
    }
    let avg = sum / k.len();
    
    println!("Avg time {}: {:?}, total elements: {:?}, unique elements: {:?}, collisions: {:?}, collisions % 512: {:?}, keys min: {:?}, avg: {:?}, max: {:?}", name, ((total as f64) / 20.0) / (corpus.len() as f64), corpus.len(), k.len(), (k.len() as f64) / (v.len() as f64), (k.len() as f64) / (v512.len() as f64), min, avg, max);
}

fn test_md5(corpus: &Vec<&str>) {
    let mut total = 0;
    let mut k = HashSet::new();
    let mut v = HashSet::new();
    for _ in 0..20 {
        v.clear();
        corpus.iter().for_each(|key| {
            k.insert(key);
            let tik = Instant::now();
            let hash = md5::compute(key);
            let tok = Instant::now();
            total += (tok - tik).as_nanos();
            v.insert(hash);
        });
    }
    println!("Avg time MD5: {:?}", ((total as f64) / 20.0) / (corpus.len() as f64))
}

fn main() {
    let corpus1 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque orci urna, pretium et porta ac, porttitor sit amet sem. Fusce sagittis lorem neque, vitae sollicitudin elit suscipit et. In interdum convallis nisl in ornare. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aliquam erat volutpat. Morbi mollis iaculis lectus ac tincidunt. Fusce nisi lacus, semper eu dignissim et, malesuada non mi. Sed euismod urna vel elit faucibus, eu bibendum ante fringilla. Curabitur tempus in turpis at mattis. Aliquam erat volutpat. Donec maximus elementum felis, sit amet dignissim augue tincidunt blandit. Aliquam fermentum, est eu mollis.".split(" ").collect::<Vec<&str>>();
    let corpus2 = "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains. But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains.But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection:".split(" ").collect::<Vec<&str>>();
    let corpus3 = "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions! A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions! A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions!A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls".split(" ").collect::<Vec<&str>>();
    let corpus4 = "Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое. Лежа на панцирнотвердой спине, он видел, стоило ему приподнять голову, свой коричневый, выпуклый, разделенный дугообразными чешуйками живот, на верхушке которого еле держалось готовое вот-вот окончательно сползти одеяло. Его многочисленные, убого тонкие по сравнению с остальным телом ножки беспомощно копошились у него перед глазами. «Что со мной случилось?» – подумал он. Это не было сном. Его комната, настоящая, разве что слишком маленькая, но обычная комната, мирно покоилась в своих четырех хорошо знакомых стенах. Над столом, где были разложены распакованные образцы сукон – Замза был коммивояжером, – висел портрет, который он недавно вырезал из иллюстрированного журнала и вставил в красивую золоченую рамку. На портрете была изображена дама в меховой шляпе и боа, она сидела очень прямо и протягивала зрителю тяжелую меховую муфту, в которой целиком исчезала ее рука. Затем взгляд Грегора устремился в окно, и пасмурная погода – слышно было, как по жести подоконника стучат капли дождя – привела его и вовсе в грустное настроение. «Хорошо бы еще немного поспать и забыть всю эту чепуху», – подумал он, но это было совершенно неосуществимо, он привык спать на правом боку, а в теперешнем своем состоянии он никак не мог принять этого положения. С какой бы силой ни поворачивался он на правый бок, он неизменно сваливался опять на спину. Закрыв глаза, чтобы не видеть своих барахтающихся ног, он проделал это добрую сотню раз и отказался от этих попыток только тогда, когда почувствовал какую-то неведомую дотоле, тупую и слабую боль в боку. «Ах ты, господи, – подумал он, – какую я выбрал хлопотную профессию! Изо дня в день в разъездах. Деловых волнений куда больше, чем на месте, в торговом доме, а кроме того, изволь терпеть тяготы дороги, думай о расписании поездов, мирись с плохим, нерегулярным питанием, завязывай со все новыми и новыми людьми недолгие, никогда не бывающие сердечными отношения. Черт бы побрал все это!» Он почувствовал вверху живота легкий зуд; медленно подвинулся на спине к прутьям кровати, чтобы удобнее было поднять голову; нашел зудевшее место, сплошь покрытое, как оказалось, белыми непонятными точечками; хотел было ощупать это место одной из ножек, но сразу отдернул ее, ибо даже простое прикосновение вызвало у него, Грегора, озноб. Он соскользнул в прежнее свое положение. «От этого раннего вставания, – подумал он, – можно совсем обезуметь. Человек должен высыпаться. Другие коммивояжеры живут, как одалиски. Когда я, например, среди дня возвращаюсь в гостиницу, чтобы переписать полученные заказы, эти господа только завтракают. А осмелься я вести себя так, мои хозяин выгнал бы меня сразу. Кто знает, впрочем, может быть, это было бы даже очень хорошо для меня. Если бы я не сдерживался ради родителей, я бы давно заявил об уходе, я бы подошел к своему хозяину и выложил ему все, что о нем думаю. Он бы так и свалился с конторки! Странная у него манера – садиться на конторку и с ее высоты разговаривать со служащим, который вдобавок вынужден подойти вплотную к конторке из-за того, что хозяин туг на ухо. Однако надежда еще не совсем потеряна: как только я накоплю денег, чтобы выплатить долг моих родителей – на это уйдет еще лет пять-шесть, – я так и поступлю. Тут-то мы и распрощаемся раз и навсегда. А пока что надо подниматься, мой поезд отходит в пять». И он взглянул на будильник, который тикал на сундуке. «Боже правый!» – подумал он. Было половина седьмого, и стрелки спокойно двигались дальше, было даже больше половины, без малого уже три четверти. Неужели будильник не звонил? С кровати было видно, что он поставлен правильно, на четыре часа; и он, несомненно, звонил. Но как можно было спокойно спать под этот сотрясающий мебель трезвон? Ну, спал-то он неспокойно, но, видимо, крепко. Однако что делать теперь? Следующий поезд уходит в семь часов; чтобы поспеть на него, он должен отчаянно торопиться, а набор образцов еще не упакован, да и сам он отнюдь не чувствует себя свежим и легким на подъем. И даже поспей он на поезд, хозяйского разноса ему все равно не избежать – ведь рассыльный торгового дома дежурил у пятичасового поезда и давно доложил о его, Грегора, опоздании. Рассыльный, человек бесхарактерный и неумный, был ставленником хозяина. А что, если сказаться больным? Но это было бы крайне неприятно и показалось бы подозрительным, ибо за пятилетнюю свою службу Грегор ни разу еще не болел. Хозяин, конечно, привел бы врача больничной кассы и стал попрекать родителей сыном-лентяем, отводя любые возражения ссылкой на этого врача, по мнению которого все люди на свете совершенно здоровы и только не любят работать. И разве в данном случае он был бы так уж неправ? Если не считать сонливости, действительно странной после такого долгого сна, Грегор и в самом деле чувствовал себя превосходно и был даже чертовски голоден.Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое. Лежа на панцирнотвердой спине, он видел, стоило ему приподнять голову, свой коричневый, выпуклый, разделенный дугообразными чешуйками живот, на верхушке которого еле держалось готовое вот-вот окончательно сползти одеяло. Его многочисленные, убого тонкие по сравнению с остальным телом ножки беспомощно копошились у него перед глазами. «Что со мной случилось?» – подумал он. Это не было сном. Его комната, настоящая, разве что слишком маленькая, но обычная комната, мирно покоилась в своих четырех хорошо знакомых стенах. Над столом, где были разложены распакованные образцы сукон – Замза был коммивояжером, – висел портрет, который он недавно вырезал из иллюстрированного журнала и вставил в красивую золоченую рамку. На портрете была изображена дама в меховой шляпе и боа, она сидела очень прямо и протягивала зрителю тяжелую меховую муфту, в которой целиком исчезала ее рука. Затем взгляд Грегора устремился в окно, и пасмурная погода – слышно было, как по жести подоконника стучат капли дождя – привела его и вовсе в грустное настроение. «Хорошо бы еще немного поспать и забыть всю эту чепуху», – подумал он, но это было совершенно неосуществимо, он привык спать на правом боку, а в теперешнем своем состоянии он никак не мог принять этого положения. С какой бы силой ни поворачивался он на правый бок, он неизменно сваливался опять на спину.".split(" ").collect::<Vec<&str>>();
    let corpus5 = "Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch. Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch. Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch.Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen".split(" ").collect::<Vec<&str>>();
    let corpus6 = "米くを舵4物委らご氏松ハナテフ月関ソ時平ふいの博情れじフ牟万い元56園フメヤオ試図ロツヤ未備王こと傷喫羅踊んゆし。栃ユヱオ書著作ユソツロ英祉業ア大課ご権質フべ空8午キ切軟づン著郎そゃす格町採ヱオマコ処8付国ムハチア究表でなだ際無ロミヱ地兵ぴげ庭体すク発抜爆位や。楽富むゆず盛航カナセ携代ハ本高きた員59今骸ンラえぜ城解イケ穴訴ぽぎ属住ヤケトヌ抱点ト広注厚でて。 国リ出難セユメ軍手ヘカウ画形サヲシ猛85用ヲキミ心死よしと身処ケヨミオ教主ーぽ事業んく字国たさょ図能シミスヤ社8板ル岡世58次戒知院んれり。市メ誘根カ数問禁竹ゃれえみ給辺のでみき今二ぎさ裕止過こクすと無32郎所ラた生展ヌヘス成度慣葬勇厘ばてか。室ゃ下携疲ム色権がぽりっ銃週ノオ姫千テム健蔵い研手ッ放容ル告属め旅側26企サノヨ宅都福ぞ通待ちぴね種脳イど労希望義通むン。 罰しい続負せ著低たル異師ユハワ東添質コチ転集ルヤ雇聴約ヒ前統らた情厳ゆさでや真胸や有披暑棚豆ゆぼたけ。盛ワセロナ情競クるっわ講3音ずをせ少地めしぜょ手63明視れに判企ヒヌエソ求総58特本ね井比ユラキ禁頭馬るゅリす能率率かがさわ。葉サソ医郡ヱヘソ労帰ナケスミ救写ワヘ株審ネヒニミ安逮イ人画ラ涯車はラ極騒りなド件5級ンかふー劇41著ぱぐ凱討だ文世ぶづどま界善魅マ渓経競融れがや。 連ーぜらご模分ッ視外ばフく運発群ほぼづ育越一ほごクけ案募ヲイソ治会イせフ製君ぜた漢村1変リヒ構5際ツ御文ヲ臭入さドぼ代書ハケ引技ろみれ回観注倉徹ぱ。論ラづ海要サ情座ゃり齢宣ラモエ芸化エマホ覧催回ら戦69本外ト葬岳な政画か連針ぴリフず。約ル闘辺ぽ経2応掲ホサアラ塾小コラ画決クノオ上室レヌヱ勝逮ぜるえむ責豊チノ明意ひけ訟6碁草メタチエ財午召喝塊む。 決めでわ名金つけレわ続人県約ぽぼす尾腹ユサ戦載リシ護賀レモフツ重涯ニ治者むんっみ職更カタチレ提話2何ワ責東まけげふ能政ヌ供禁がびてわ提改倶れめ。読み担後ぽ安加ぎ論鹿ツ統最お気麻月つじもあ竜思いろめ判必満理トコ文連ムイウハ寄串ざほびー。文ゆこっ向27年メイ便能ノセヲ待1王スねたゆ伝派んね点過カト治読よにきべ使人スシ都言え阻8割べづえみ注引敷的岳犠眠どそ。 学用イだ医客開ロ供界もぞだ実隆モイヌ務坂ナコヲ権野ろづ初場ぱ低会づぱじ新倒コ化政レ止奮浸猪ッわえづ。形いやリ要帰ほまむだ業領スル必打さ島14巻リ集日ネヘホタ面幅ち写上そぴ円図ムタコモ報使イわざと会催ヤヲ康証をドぶレ盤岡ホハツ作29管しをめ公問懐蓄っさ。来ゆぼあぱ投秋シ語右ぐ身靖かば辛握捕家記ヘワ神岐囲づ毘観メテクツ政73夕罪57需93誌飲査仁さ。 変レめ束球よんま会特ヱコ聞重だ史純ーどる件32浦レぴよゃ上強ネラリロ査従セユヤ専棋光レ作表ひぶ予正ぜーな誉確フス函6報円ス進治ね能営済否雄でわょ。42生型ば着続ア短実ぎおめび前環闘ラヤヲル診均っとにの声公トヱテマ整試椅情久妊舌頃ざとっく。品キチトテ阿国ラら受87世ヲフセリ川86個ーょぼげ危子ヘレカメ無会ぱかへ事通んかて電条ロツ徴商ぶぞそを居暑メ害広せもがり禁応レミヲ応響割壮憶はぱ。 千れンが織財メニ況界ネトレミ学豊フオホシ近月レたやご的罪ょな菱技ちる警栗エセ提89林危氷48参ア説森クキヒヱ薬社ホコエリ負和ルび紀下ケミイ掲歳特ごず扱底ク護木連ちクを各形ばすか。変ぱなれ町7融ヌ街準以タユヘム質裕ぶで遺語俊ぎずょ事金文キ写多山ーゆに歩帯すで会世クぜよ論写ヲ達71林危氷5間続ぎぜび高怠す。 係8青け応著ミ戦条ナヘネカ思79未ぎ算伊をゃ泉人ーづ需説っ畑鹿27軽ラソツ権2促千護ルロナカ開国ケ暴嶋ご池表だ。佐フナ訪麻はてせば勝効をあ医戦画とさわぴ者両すいあ並来んば載食ぴ件友頂業へえぞ魚祝ネラ聞率スコリケ始全ンこび夫出ドふ今布うぎふゅ実克即哉循やしんな。 暮す備54依紀てッん末刊と柔称むてス無府ケイ変壌をぱ汁連フマス海世ヌ中負知問ナヘケ純推ひ読着ヒ言若私軽れ。掲けフむ王本オコ線人をっさ必和断セソヲハ図芸ちかな防長りぶは投新意相ツ並5余セ職岳ぞ端古空援そ。森ヨエチ題5東っ自兄ち暴5近鹿横ト的京ハ安氷ナキ深際ぎ並節くスむの権工ほルせ京49効タムチ処三ぞぴラ済国ずっ文経ヘトミ水分準そが。".split(" ").collect::<Vec<&str>>();
    let corpus7 = "AbortMultipartUpload CompleteMultipartUpload CopyObject CreateBucket CreateMultipartUpload DeleteBucket DeleteBucketAnalyticsConfiguration DeleteBucketCors DeleteBucketEncryption DeleteBucketIntelligentTieringConfiguration DeleteBucketInventoryConfiguration DeleteBucketLifecycle DeleteBucketMetricsConfiguration DeleteBucketOwnershipControls DeleteBucketPolicy DeleteBucketReplication DeleteBucketTagging DeleteBucketWebsite DeleteObject DeleteObjects DeleteObjectTagging DeletePublicAccessBlock GetBucketAccelerateConfiguration GetBucketAcl GetBucketAnalyticsConfiguration GetBucketCors GetBucketEncryption GetBucketIntelligentTieringConfiguration GetBucketInventoryConfiguration GetBucketLifecycle GetBucketLifecycleConfiguration GetBucketLocation GetBucketLogging GetBucketMetricsConfiguration GetBucketNotification GetBucketNotificationConfiguration GetBucketOwnershipControls GetBucketPolicy GetBucketPolicyStatus GetBucketReplication GetBucketRequestPayment GetBucketTagging GetBucketVersioning GetBucketWebsite GetObject GetObjectAcl GetObjectAttributes GetObjectLegalHold GetObjectLockConfiguration GetObjectRetention GetObjectTagging GetObjectTorrent GetPublicAccessBlock HeadBucket HeadObject ListBucketAnalyticsConfigurations ListBucketIntelligentTieringConfigurations ListBucketInventoryConfigurations ListBucketMetricsConfigurations ListBuckets ListMultipartUploads ListObjects ListObjectsV2 ListObjectVersions ListParts PutBucketAccelerateConfiguration PutBucketAcl PutBucketAnalyticsConfiguration PutBucketCors PutBucketEncryption PutBucketIntelligentTieringConfiguration PutBucketInventoryConfiguration PutBucketLifecycle PutBucketLifecycleConfiguration PutBucketLogging PutBucketMetricsConfiguration PutBucketNotification PutBucketNotificationConfiguration PutBucketOwnershipControls PutBucketPolicy PutBucketReplication PutBucketRequestPayment PutBucketTagging PutBucketVersioning PutBucketWebsite PutObject PutObjectAcl PutObjectLegalHold PutObjectLockConfiguration PutObjectRetention PutObjectTagging PutPublicAccessBlock RestoreObject SelectObjectContent UploadPart UploadPartCopy WriteGetObjectResponse CreateAccessPoint CreateAccessPointForObjectLambda CreateBucket CreateJob CreateMultiRegionAccessPoint DeleteAccessPoint DeleteAccessPointForObjectLambda DeleteAccessPointPolicy DeleteAccessPointPolicyForObjectLambda DeleteBucket DeleteBucketLifecycleConfiguration DeleteBucketPolicy DeleteBucketReplication DeleteBucketTagging DeleteJobTagging DeleteMultiRegionAccessPoint DeletePublicAccessBlock DeleteStorageLensConfiguration DeleteStorageLensConfigurationTagging DescribeJob DescribeMultiRegionAccessPointOperation GetAccessPoint GetAccessPointConfigurationForObjectLambda GetAccessPointForObjectLambda GetAccessPointPolicy GetAccessPointPolicyForObjectLambda GetAccessPointPolicyStatus GetAccessPointPolicyStatusForObjectLambda GetBucket GetBucketLifecycleConfiguration GetBucketPolicy GetBucketReplication GetBucketTagging GetBucketVersioning GetJobTagging GetMultiRegionAccessPoint GetMultiRegionAccessPointPolicy GetMultiRegionAccessPointPolicyStatus GetMultiRegionAccessPointRoutes GetPublicAccessBlock GetStorageLensConfiguration GetStorageLensConfigurationTagging ListAccessPoints ListAccessPointsForObjectLambda ListJobs ListMultiRegionAccessPoints ListRegionalBuckets ListStorageLensConfigurations PutAccessPointConfigurationForObjectLambda PutAccessPointPolicy PutAccessPointPolicyForObjectLambda PutBucketLifecycleConfiguration PutBucketPolicy PutBucketReplication PutBucketTagging PutBucketVersioning PutJobTagging PutMultiRegionAccessPointPolicy PutPublicAccessBlock PutStorageLensConfiguration PutStorageLensConfigurationTagging SubmitMultiRegionAccessPointRoutes UpdateJobPriority UpdateJobStatus".split(" ").collect::<Vec<&str>>();
    

    let corps = vec![corpus1, corpus2, corpus3, corpus4, corpus5, corpus6, corpus7];
    for corpus in corps {
        test_hashing::<DefaultHasher>(&corpus, "Default");
        test_hashing::<FxHasher64>(&corpus, "FxHasher");
        test_hashing::<AHasher>(&corpus, "AHasher");
        test_hashing::<WyHash>(&corpus, "WyHash");
        test_md5(&corpus);
    }

    let contents = fs::read_to_string("/usr/share/dict/words")
        .expect("Should have been able to read the file");
    
    let tik = Instant::now();
    let hash = md5::compute(contents);
    let tok = Instant::now();

    println!("MD5: {:?}", hash);
    println!("In {:?}", (tok - tik).as_nanos());
    
    // let hash = wyhash_single(b"Maxim", 0);
    // println!("{:?}: {:?}", "Maxim", hash);

    let mut hasher = AHasher::default();
    hasher.write(b"Maxim");
    println!("Maxim: {}", hasher.finish());
}


--- benchmark_other_languages/swift/hash_functions/.gitignore ---

.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/config/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc


--- benchmark_other_languages/swift/hash_functions/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ---

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>IDEDidComputeMac32BitWarning</key>
	<true/>
</dict>
</plist>


--- benchmark_other_languages/swift/hash_functions/.swiftpm/xcode/xcshareddata/xcschemes/hash_functions.xcscheme ---

<?xml version="1.0" encoding="UTF-8"?>
<Scheme
   LastUpgradeVersion = "1430"
   version = "1.7">
   <BuildAction
      parallelizeBuildables = "YES"
      buildImplicitDependencies = "YES">
      <BuildActionEntries>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "hash_functions"
               BuildableName = "hash_functions"
               BlueprintName = "hash_functions"
               ReferencedContainer = "container:">
            </BuildableReference>
         </BuildActionEntry>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "NO"
            buildForArchiving = "NO"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "hash_functionsTests"
               BuildableName = "hash_functionsTests"
               BlueprintName = "hash_functionsTests"
               ReferencedContainer = "container:">
            </BuildableReference>
         </BuildActionEntry>
      </BuildActionEntries>
   </BuildAction>
   <TestAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      shouldUseLaunchSchemeArgsEnv = "YES"
      shouldAutocreateTestPlan = "YES">
      <Testables>
         <TestableReference
            skipped = "NO">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "hash_functionsTests"
               BuildableName = "hash_functionsTests"
               BlueprintName = "hash_functionsTests"
               ReferencedContainer = "container:">
            </BuildableReference>
         </TestableReference>
      </Testables>
   </TestAction>
   <LaunchAction
      buildConfiguration = "Release"
      selectedDebuggerIdentifier = ""
      selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
      launchStyle = "0"
      useCustomWorkingDirectory = "NO"
      ignoresPersistentStateOnLaunch = "NO"
      debugDocumentVersioning = "YES"
      debugServiceExtension = "internal"
      allowLocationSimulation = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "hash_functions"
            BuildableName = "hash_functions"
            BlueprintName = "hash_functions"
            ReferencedContainer = "container:">
         </BuildableReference>
      </BuildableProductRunnable>
   </LaunchAction>
   <ProfileAction
      buildConfiguration = "Release"
      shouldUseLaunchSchemeArgsEnv = "YES"
      savedToolIdentifier = ""
      useCustomWorkingDirectory = "NO"
      debugDocumentVersioning = "YES">
      <MacroExpansion>
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "hash_functions"
            BuildableName = "hash_functions"
            BlueprintName = "hash_functions"
            ReferencedContainer = "container:">
         </BuildableReference>
      </MacroExpansion>
   </ProfileAction>
   <AnalyzeAction
      buildConfiguration = "Debug">
   </AnalyzeAction>
   <ArchiveAction
      buildConfiguration = "Release"
      revealArchiveInOrganizer = "YES">
   </ArchiveAction>
</Scheme>


--- benchmark_other_languages/swift/hash_functions/Package.swift ---

// swift-tools-version: 5.8
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "hash_functions",
    products: [
        .executable(
            name: "hash_functions",
            targets: ["hash_functions"]),
    ],
    dependencies: [],
    targets: [
        .executableTarget(
            name: "hash_functions",
            dependencies: [])
    ]
)


--- benchmark_other_languages/swift/hash_functions/README.md ---

# hash_functions

Benachmark for default hash function.

Installing Swift on Ubunutu https://gist.github.com/Jswizzy/408af5829970f9eb18f9b45f891910bb (pick the latest version, tried with 5.9.2)

Run the benchamrk with `swift run --configuration release`


--- benchmark_other_languages/swift/hash_functions/Sources/hash_functions/main.swift ---

//
//  File.swift
//  
//
//  Created by Maxim Zaks on 23.12.23.
//

import Foundation
import Dispatch

func hash(corpus: [Substring]) {
    var total: UInt64 = 0
    var k = Set<Substring>()
    var v = Set<Int>()
    var v512 = Set<Int>()
    for _ in 0..<20 {
        v.removeAll()
        v512.removeAll()
        for key in corpus {
            k.insert(key)
            let tik = DispatchTime.now()
            let h = key.hash
            let tok = DispatchTime.now()
            total += (tok.uptimeNanoseconds - tik.uptimeNanoseconds)
            v.insert(h)
            v512.insert(h % 512)
        }
    }
    var min = Int.max
    var max = Int.min
    var sum = 0
    for key in k {
        let l = key.count
        sum += l
        if l < min {
            min = l
        }
        if l > max {
            max = l
        }
    }
    let avg = Int(sum / k.count)
    let avgTimeInNs = ((Double(total) / Double(20.0)) / Double(corpus.count))

    print("Avg time: \(avgTimeInNs), total elements: \(corpus.count), unique elements: \(k.count), collisions: \(Double(k.count) / Double(v.count)), collisions % 512: \(Double(k.count) / Double(v512.count)), keys min: \(min), avg: \(avg), max: \(max)")
}


func benchmark() {
    let corpus1 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque orci urna, pretium et porta ac, porttitor sit amet sem. Fusce sagittis lorem neque, vitae sollicitudin elit suscipit et. In interdum convallis nisl in ornare. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aliquam erat volutpat. Morbi mollis iaculis lectus ac tincidunt. Fusce nisi lacus, semper eu dignissim et, malesuada non mi. Sed euismod urna vel elit faucibus, eu bibendum ante fringilla. Curabitur tempus in turpis at mattis. Aliquam erat volutpat. Donec maximus elementum felis, sit amet dignissim augue tincidunt blandit. Aliquam fermentum, est eu mollis.".split(separator: " ")
    let corpus2 = "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains. But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains.But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection:".split(separator: " ")
    let corpus3 = "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions! A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions! A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions!A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls".split(separator: " ")
    let corpus4 = "Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое. Лежа на панцирнотвердой спине, он видел, стоило ему приподнять голову, свой коричневый, выпуклый, разделенный дугообразными чешуйками живот, на верхушке которого еле держалось готовое вот-вот окончательно сползти одеяло. Его многочисленные, убого тонкие по сравнению с остальным телом ножки беспомощно копошились у него перед глазами. «Что со мной случилось?» – подумал он. Это не было сном. Его комната, настоящая, разве что слишком маленькая, но обычная комната, мирно покоилась в своих четырех хорошо знакомых стенах. Над столом, где были разложены распакованные образцы сукон – Замза был коммивояжером, – висел портрет, который он недавно вырезал из иллюстрированного журнала и вставил в красивую золоченую рамку. На портрете была изображена дама в меховой шляпе и боа, она сидела очень прямо и протягивала зрителю тяжелую меховую муфту, в которой целиком исчезала ее рука. Затем взгляд Грегора устремился в окно, и пасмурная погода – слышно было, как по жести подоконника стучат капли дождя – привела его и вовсе в грустное настроение. «Хорошо бы еще немного поспать и забыть всю эту чепуху», – подумал он, но это было совершенно неосуществимо, он привык спать на правом боку, а в теперешнем своем состоянии он никак не мог принять этого положения. С какой бы силой ни поворачивался он на правый бок, он неизменно сваливался опять на спину. Закрыв глаза, чтобы не видеть своих барахтающихся ног, он проделал это добрую сотню раз и отказался от этих попыток только тогда, когда почувствовал какую-то неведомую дотоле, тупую и слабую боль в боку. «Ах ты, господи, – подумал он, – какую я выбрал хлопотную профессию! Изо дня в день в разъездах. Деловых волнений куда больше, чем на месте, в торговом доме, а кроме того, изволь терпеть тяготы дороги, думай о расписании поездов, мирись с плохим, нерегулярным питанием, завязывай со все новыми и новыми людьми недолгие, никогда не бывающие сердечными отношения. Черт бы побрал все это!» Он почувствовал вверху живота легкий зуд; медленно подвинулся на спине к прутьям кровати, чтобы удобнее было поднять голову; нашел зудевшее место, сплошь покрытое, как оказалось, белыми непонятными точечками; хотел было ощупать это место одной из ножек, но сразу отдернул ее, ибо даже простое прикосновение вызвало у него, Грегора, озноб. Он соскользнул в прежнее свое положение. «От этого раннего вставания, – подумал он, – можно совсем обезуметь. Человек должен высыпаться. Другие коммивояжеры живут, как одалиски. Когда я, например, среди дня возвращаюсь в гостиницу, чтобы переписать полученные заказы, эти господа только завтракают. А осмелься я вести себя так, мои хозяин выгнал бы меня сразу. Кто знает, впрочем, может быть, это было бы даже очень хорошо для меня. Если бы я не сдерживался ради родителей, я бы давно заявил об уходе, я бы подошел к своему хозяину и выложил ему все, что о нем думаю. Он бы так и свалился с конторки! Странная у него манера – садиться на конторку и с ее высоты разговаривать со служащим, который вдобавок вынужден подойти вплотную к конторке из-за того, что хозяин туг на ухо. Однако надежда еще не совсем потеряна: как только я накоплю денег, чтобы выплатить долг моих родителей – на это уйдет еще лет пять-шесть, – я так и поступлю. Тут-то мы и распрощаемся раз и навсегда. А пока что надо подниматься, мой поезд отходит в пять». И он взглянул на будильник, который тикал на сундуке. «Боже правый!» – подумал он. Было половина седьмого, и стрелки спокойно двигались дальше, было даже больше половины, без малого уже три четверти. Неужели будильник не звонил? С кровати было видно, что он поставлен правильно, на четыре часа; и он, несомненно, звонил. Но как можно было спокойно спать под этот сотрясающий мебель трезвон? Ну, спал-то он неспокойно, но, видимо, крепко. Однако что делать теперь? Следующий поезд уходит в семь часов; чтобы поспеть на него, он должен отчаянно торопиться, а набор образцов еще не упакован, да и сам он отнюдь не чувствует себя свежим и легким на подъем. И даже поспей он на поезд, хозяйского разноса ему все равно не избежать – ведь рассыльный торгового дома дежурил у пятичасового поезда и давно доложил о его, Грегора, опоздании. Рассыльный, человек бесхарактерный и неумный, был ставленником хозяина. А что, если сказаться больным? Но это было бы крайне неприятно и показалось бы подозрительным, ибо за пятилетнюю свою службу Грегор ни разу еще не болел. Хозяин, конечно, привел бы врача больничной кассы и стал попрекать родителей сыном-лентяем, отводя любые возражения ссылкой на этого врача, по мнению которого все люди на свете совершенно здоровы и только не любят работать. И разве в данном случае он был бы так уж неправ? Если не считать сонливости, действительно странной после такого долгого сна, Грегор и в самом деле чувствовал себя превосходно и был даже чертовски голоден.Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое. Лежа на панцирнотвердой спине, он видел, стоило ему приподнять голову, свой коричневый, выпуклый, разделенный дугообразными чешуйками живот, на верхушке которого еле держалось готовое вот-вот окончательно сползти одеяло. Его многочисленные, убого тонкие по сравнению с остальным телом ножки беспомощно копошились у него перед глазами. «Что со мной случилось?» – подумал он. Это не было сном. Его комната, настоящая, разве что слишком маленькая, но обычная комната, мирно покоилась в своих четырех хорошо знакомых стенах. Над столом, где были разложены распакованные образцы сукон – Замза был коммивояжером, – висел портрет, который он недавно вырезал из иллюстрированного журнала и вставил в красивую золоченую рамку. На портрете была изображена дама в меховой шляпе и боа, она сидела очень прямо и протягивала зрителю тяжелую меховую муфту, в которой целиком исчезала ее рука. Затем взгляд Грегора устремился в окно, и пасмурная погода – слышно было, как по жести подоконника стучат капли дождя – привела его и вовсе в грустное настроение. «Хорошо бы еще немного поспать и забыть всю эту чепуху», – подумал он, но это было совершенно неосуществимо, он привык спать на правом боку, а в теперешнем своем состоянии он никак не мог принять этого положения. С какой бы силой ни поворачивался он на правый бок, он неизменно сваливался опять на спину.".split(separator: " ")
    let corpus5 = "Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch. Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch. Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort \"und\" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch.Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen".split(separator: " ")
    let corpus6 = "米くを舵4物委らご氏松ハナテフ月関ソ時平ふいの博情れじフ牟万い元56園フメヤオ試図ロツヤ未備王こと傷喫羅踊んゆし。栃ユヱオ書著作ユソツロ英祉業ア大課ご権質フべ空8午キ切軟づン著郎そゃす格町採ヱオマコ処8付国ムハチア究表でなだ際無ロミヱ地兵ぴげ庭体すク発抜爆位や。楽富むゆず盛航カナセ携代ハ本高きた員59今骸ンラえぜ城解イケ穴訴ぽぎ属住ヤケトヌ抱点ト広注厚でて。 国リ出難セユメ軍手ヘカウ画形サヲシ猛85用ヲキミ心死よしと身処ケヨミオ教主ーぽ事業んく字国たさょ図能シミスヤ社8板ル岡世58次戒知院んれり。市メ誘根カ数問禁竹ゃれえみ給辺のでみき今二ぎさ裕止過こクすと無32郎所ラた生展ヌヘス成度慣葬勇厘ばてか。室ゃ下携疲ム色権がぽりっ銃週ノオ姫千テム健蔵い研手ッ放容ル告属め旅側26企サノヨ宅都福ぞ通待ちぴね種脳イど労希望義通むン。 罰しい続負せ著低たル異師ユハワ東添質コチ転集ルヤ雇聴約ヒ前統らた情厳ゆさでや真胸や有披暑棚豆ゆぼたけ。盛ワセロナ情競クるっわ講3音ずをせ少地めしぜょ手63明視れに判企ヒヌエソ求総58特本ね井比ユラキ禁頭馬るゅリす能率率かがさわ。葉サソ医郡ヱヘソ労帰ナケスミ救写ワヘ株審ネヒニミ安逮イ人画ラ涯車はラ極騒りなド件5級ンかふー劇41著ぱぐ凱討だ文世ぶづどま界善魅マ渓経競融れがや。 連ーぜらご模分ッ視外ばフく運発群ほぼづ育越一ほごクけ案募ヲイソ治会イせフ製君ぜた漢村1変リヒ構5際ツ御文ヲ臭入さドぼ代書ハケ引技ろみれ回観注倉徹ぱ。論ラづ海要サ情座ゃり齢宣ラモエ芸化エマホ覧催回ら戦69本外ト葬岳な政画か連針ぴリフず。約ル闘辺ぽ経2応掲ホサアラ塾小コラ画決クノオ上室レヌヱ勝逮ぜるえむ責豊チノ明意ひけ訟6碁草メタチエ財午召喝塊む。 決めでわ名金つけレわ続人県約ぽぼす尾腹ユサ戦載リシ護賀レモフツ重涯ニ治者むんっみ職更カタチレ提話2何ワ責東まけげふ能政ヌ供禁がびてわ提改倶れめ。読み担後ぽ安加ぎ論鹿ツ統最お気麻月つじもあ竜思いろめ判必満理トコ文連ムイウハ寄串ざほびー。文ゆこっ向27年メイ便能ノセヲ待1王スねたゆ伝派んね点過カト治読よにきべ使人スシ都言え阻8割べづえみ注引敷的岳犠眠どそ。 学用イだ医客開ロ供界もぞだ実隆モイヌ務坂ナコヲ権野ろづ初場ぱ低会づぱじ新倒コ化政レ止奮浸猪ッわえづ。形いやリ要帰ほまむだ業領スル必打さ島14巻リ集日ネヘホタ面幅ち写上そぴ円図ムタコモ報使イわざと会催ヤヲ康証をドぶレ盤岡ホハツ作29管しをめ公問懐蓄っさ。来ゆぼあぱ投秋シ語右ぐ身靖かば辛握捕家記ヘワ神岐囲づ毘観メテクツ政73夕罪57需93誌飲査仁さ。 変レめ束球よんま会特ヱコ聞重だ史純ーどる件32浦レぴよゃ上強ネラリロ査従セユヤ専棋光レ作表ひぶ予正ぜーな誉確フス函6報円ス進治ね能営済否雄でわょ。42生型ば着続ア短実ぎおめび前環闘ラヤヲル診均っとにの声公トヱテマ整試椅情久妊舌頃ざとっく。品キチトテ阿国ラら受87世ヲフセリ川86個ーょぼげ危子ヘレカメ無会ぱかへ事通んかて電条ロツ徴商ぶぞそを居暑メ害広せもがり禁応レミヲ応響割壮憶はぱ。 千れンが織財メニ況界ネトレミ学豊フオホシ近月レたやご的罪ょな菱技ちる警栗エセ提89林危氷48参ア説森クキヒヱ薬社ホコエリ負和ルび紀下ケミイ掲歳特ごず扱底ク護木連ちクを各形ばすか。変ぱなれ町7融ヌ街準以タユヘム質裕ぶで遺語俊ぎずょ事金文キ写多山ーゆに歩帯すで会世クぜよ論写ヲ達71林危氷5間続ぎぜび高怠す。 係8青け応著ミ戦条ナヘネカ思79未ぎ算伊をゃ泉人ーづ需説っ畑鹿27軽ラソツ権2促千護ルロナカ開国ケ暴嶋ご池表だ。佐フナ訪麻はてせば勝効をあ医戦画とさわぴ者両すいあ並来んば載食ぴ件友頂業へえぞ魚祝ネラ聞率スコリケ始全ンこび夫出ドふ今布うぎふゅ実克即哉循やしんな。 暮す備54依紀てッん末刊と柔称むてス無府ケイ変壌をぱ汁連フマス海世ヌ中負知問ナヘケ純推ひ読着ヒ言若私軽れ。掲けフむ王本オコ線人をっさ必和断セソヲハ図芸ちかな防長りぶは投新意相ツ並5余セ職岳ぞ端古空援そ。森ヨエチ題5東っ自兄ち暴5近鹿横ト的京ハ安氷ナキ深際ぎ並節くスむの権工ほルせ京49効タムチ処三ぞぴラ済国ずっ文経ヘトミ水分準そが。".split(separator: " ")
    let corpus7 = "AbortMultipartUpload CompleteMultipartUpload CopyObject CreateBucket CreateMultipartUpload DeleteBucket DeleteBucketAnalyticsConfiguration DeleteBucketCors DeleteBucketEncryption DeleteBucketIntelligentTieringConfiguration DeleteBucketInventoryConfiguration DeleteBucketLifecycle DeleteBucketMetricsConfiguration DeleteBucketOwnershipControls DeleteBucketPolicy DeleteBucketReplication DeleteBucketTagging DeleteBucketWebsite DeleteObject DeleteObjects DeleteObjectTagging DeletePublicAccessBlock GetBucketAccelerateConfiguration GetBucketAcl GetBucketAnalyticsConfiguration GetBucketCors GetBucketEncryption GetBucketIntelligentTieringConfiguration GetBucketInventoryConfiguration GetBucketLifecycle GetBucketLifecycleConfiguration GetBucketLocation GetBucketLogging GetBucketMetricsConfiguration GetBucketNotification GetBucketNotificationConfiguration GetBucketOwnershipControls GetBucketPolicy GetBucketPolicyStatus GetBucketReplication GetBucketRequestPayment GetBucketTagging GetBucketVersioning GetBucketWebsite GetObject GetObjectAcl GetObjectAttributes GetObjectLegalHold GetObjectLockConfiguration GetObjectRetention GetObjectTagging GetObjectTorrent GetPublicAccessBlock HeadBucket HeadObject ListBucketAnalyticsConfigurations ListBucketIntelligentTieringConfigurations ListBucketInventoryConfigurations ListBucketMetricsConfigurations ListBuckets ListMultipartUploads ListObjects ListObjectsV2 ListObjectVersions ListParts PutBucketAccelerateConfiguration PutBucketAcl PutBucketAnalyticsConfiguration PutBucketCors PutBucketEncryption PutBucketIntelligentTieringConfiguration PutBucketInventoryConfiguration PutBucketLifecycle PutBucketLifecycleConfiguration PutBucketLogging PutBucketMetricsConfiguration PutBucketNotification PutBucketNotificationConfiguration PutBucketOwnershipControls PutBucketPolicy PutBucketReplication PutBucketRequestPayment PutBucketTagging PutBucketVersioning PutBucketWebsite PutObject PutObjectAcl PutObjectLegalHold PutObjectLockConfiguration PutObjectRetention PutObjectTagging PutPublicAccessBlock RestoreObject SelectObjectContent UploadPart UploadPartCopy WriteGetObjectResponse CreateAccessPoint CreateAccessPointForObjectLambda CreateBucket CreateJob CreateMultiRegionAccessPoint DeleteAccessPoint DeleteAccessPointForObjectLambda DeleteAccessPointPolicy DeleteAccessPointPolicyForObjectLambda DeleteBucket DeleteBucketLifecycleConfiguration DeleteBucketPolicy DeleteBucketReplication DeleteBucketTagging DeleteJobTagging DeleteMultiRegionAccessPoint DeletePublicAccessBlock DeleteStorageLensConfiguration DeleteStorageLensConfigurationTagging DescribeJob DescribeMultiRegionAccessPointOperation GetAccessPoint GetAccessPointConfigurationForObjectLambda GetAccessPointForObjectLambda GetAccessPointPolicy GetAccessPointPolicyForObjectLambda GetAccessPointPolicyStatus GetAccessPointPolicyStatusForObjectLambda GetBucket GetBucketLifecycleConfiguration GetBucketPolicy GetBucketReplication GetBucketTagging GetBucketVersioning GetJobTagging GetMultiRegionAccessPoint GetMultiRegionAccessPointPolicy GetMultiRegionAccessPointPolicyStatus GetMultiRegionAccessPointRoutes GetPublicAccessBlock GetStorageLensConfiguration GetStorageLensConfigurationTagging ListAccessPoints ListAccessPointsForObjectLambda ListJobs ListMultiRegionAccessPoints ListRegionalBuckets ListStorageLensConfigurations PutAccessPointConfigurationForObjectLambda PutAccessPointPolicy PutAccessPointPolicyForObjectLambda PutBucketLifecycleConfiguration PutBucketPolicy PutBucketReplication PutBucketTagging PutBucketVersioning PutJobTagging PutMultiRegionAccessPointPolicy PutPublicAccessBlock PutStorageLensConfiguration PutStorageLensConfigurationTagging SubmitMultiRegionAccessPointRoutes UpdateJobPriority UpdateJobStatus".split(separator: " ")
    let corps = [corpus1, corpus2, corpus3, corpus4, corpus5, corpus6, corpus7]
    for corpus in corps {
        hash(corpus: corpus)
    }
}

benchmark()


--- checkout_remote_modules.sh ---

#!/bin/bash

function check_out_remote_module() (
    rurl="$1"
    shift
    declare -a paths
    declare -a module_names
    for var in "$@" 
    do
        IFS="="
        read -ra module_name_components <<< "$var"
        components_count=${#module_name_components[@]}
        path=${module_name_components[0]}
        module_name=${module_name_components[$components_count-1]}
        paths=("${paths[@]}" "$path")
        module_names=("${module_names[@]}" "$module_name")
    done
    IFS=" "

    for module_name in "${module_names[@]}" 
    do
        rm -rf ../$module_name
    done
    
    current_date_time=$(date)
    echo "URL: $rurl"
    git clone -n --depth=1 --filter=tree:0 $rurl
    cd ${rurl##*/}
    git sparse-checkout set --no-cone "${paths[@]}"
    git checkout
    
    for i in "${!paths[@]}" 
    do
        module_name=${module_names[$i]}
        path=${paths[$i]}
        cp -R ./$path ../../$module_name
        echo $current_date_time > ../../$module_name/.checkoutinfo
        echo "URL: $rurl" >> ../../$module_name/.checkoutinfo
        echo "Path: $path" >> ../../$module_name/.checkoutinfo
    done
    cd ../
)

function checkout()(
    # Add check out remote module calls here
    
    # check_out_remote_module "https://github.com/mzaks/mojo-trees" "fiby_tree"
)

mkdir -p "_deps"
cd "_deps"

checkout

rm -rf "../_deps"

--- fnv1a/__init__.mojo ---

from .fnv1a import fnv1a32, fnv1a64

--- fnv1a/fnv1a.mojo ---

alias fnv_32_prime: UInt32 = 0x01000193
alias fnv_32_offset_bassis: UInt32 = 0x811c9dc5
alias fnv_64_prime = 0x100000001b3
alias fnv_64_offset_bassis = 0xcbf29ce484222325


@always_inline
fn fnv1a32(s: String) -> UInt32:
    var hash = fnv_32_offset_bassis
    var buffer = UnsafePointer(s.unsafe_ptr())
    for i in range(len(s)):
        hash ^= buffer.load(i).cast[DType.uint32]()
        hash *= fnv_32_prime
    return hash

@always_inline
fn fnv1a64(s: String) -> UInt64:
    var hash: UInt64 = fnv_64_offset_bassis
    var buffer = UnsafePointer(s.unsafe_ptr())
    for i in range(len(s)):
        hash ^= buffer.load(i).cast[DType.uint64]()
        hash *= fnv_64_prime
    return hash

--- fxhash/__init__.mojo ---

from .fxhash import fxhash32, fxhash64


--- fxhash/fxhash.mojo ---

from bit import rotate_bits_left

alias ROTATE = 5
alias SEED64 = 0x51_7c_c1_b7_27_22_0a_95
alias SEED32 = 0x9e_37_79_b9

@always_inline
fn fxhash32(s: String, seed: UInt32 = 0) -> UInt32:
    var bytes = UnsafePointer(s.unsafe_ptr())
    var count = len(s)
    var hash = seed
    while count >= 4:
        hash = _hash_word32(hash, bytes.bitcast[DType.uint32]().load())
        bytes = bytes.offset(4)
        count -= 4
    if count >= 2:
        hash = _hash_word32(hash, bytes.bitcast[DType.uint16]().load().cast[DType.uint32]())
        bytes = bytes.offset(2)
        count -= 2
    if count > 0:
        hash = _hash_word32(hash, bytes.load().cast[DType.uint32]())
    return hash

@always_inline
fn fxhash64(s: String, seed: UInt64 = 0) -> UInt64:
    var bytes = UnsafePointer(s.unsafe_ptr())
    var count = len(s)
    var hash = seed
    while count >= 8:
        hash = _hash_word64(hash, bytes.bitcast[DType.uint64]().load())
        bytes = bytes.offset(8)
        count -= 8
    if count >= 4:
        hash = _hash_word64(hash, bytes.bitcast[DType.uint32]().load().cast[DType.uint64]())
        bytes = bytes.offset(4)
        count -= 4
    if count >= 2:
        hash = _hash_word64(hash, bytes.bitcast[DType.uint16]().load().cast[DType.uint64]())
        bytes = bytes.offset(2)
        count -= 2
    if count > 0:
        hash = _hash_word64(hash, bytes.load().cast[DType.uint64]())
    return hash


@always_inline
fn _hash_word32(value: UInt32, word: UInt32) -> UInt32:
    return (rotate_bits_left[ROTATE](value) ^ word) * SEED32

@always_inline
fn _hash_word64(value: UInt64, word: UInt64) -> UInt64:
    return (rotate_bits_left[ROTATE](value) ^ word) * SEED64


--- md5/__init__.mojo ---

from .md5 import md5_string

--- md5/md5.mojo ---

# Based on https://github.com/Zunawe/md5-c

from utils.loop import unroll
from memory.unsafe import bitcast
from memory import memset_zero
from bit import rotate_bits_left

alias S = SIMD[DType.uint32, 64](
    7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
    5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,
    4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
    6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
)

alias K = SIMD[DType.uint32, 64](
    0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
    0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
    0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
    0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
    0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
    0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
    0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
    0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
    0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
    0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
    0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
    0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
    0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
    0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
    0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
    0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
)

alias PADDING = create_padding()

fn create_padding() -> UnsafePointer[UInt8]:
    var result = UnsafePointer[UInt8].alloc(64)
    result.store(0, 0x80)
    for i in range(1, 64):
        result.store(i, 0)
    return result

struct Md5Context:
    var buffer: SIMD[DType.uint32, 4]
    var input: SIMD[DType.uint8, 64]
    var digest: SIMD[DType.uint8, 16]
    var size: UInt64

    fn __init__(inout self):
        self.size = 0
        self.buffer = SIMD[DType.uint32, 4](0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476)
        self.input = SIMD[DType.uint8, 64]()
        self.digest = SIMD[DType.uint8, 16]()

    @always_inline
    fn update(inout self, input_buffer: UnsafePointer[UInt8], length: Int):
        var offset = int(self.size & 63)
        var input = SIMD[DType.uint32, 16]()
        self.size += length

        for i in range(length):
            self.input[offset] = input_buffer.offset(i).load()
            offset += 1
            if offset & 63 == 0:
                # TODO: check if it works on BigEndian arch (or needs bswap?)
                input = bitcast[DType.uint32, 16](self.input)
                self.step(input)
                offset = 0
    
    @always_inline
    fn finalize(owned self) -> SIMD[DType.uint8, 16]:
        var input = SIMD[DType.uint32, 16]()
        var offset = int(self.size & 63)
        var padding_length = 56 - offset if offset < 56 else 56 + 64 - offset

        self.update(PADDING, padding_length)
        self.size -= padding_length
        input = bitcast[DType.uint32, 16](self.input)
        input[14] = (self.size * 8).cast[DType.uint32]()
        input[15] = ((self.size * 8) >> 32).cast[DType.uint32]()
        self.step(input)
        return bitcast[DType.uint8, 16](self.buffer)

    @always_inline
    fn step(inout self, input: SIMD[DType.uint32, 16]):
        var aa = self.buffer[0]
        var bb = self.buffer[1]
        var cc = self.buffer[2]
        var dd = self.buffer[3]

        var e: UInt32 = 0
        var j = 0

        @parameter
        fn shuffle[i: Int]():
            alias step = i >> 4
            @parameter
            if step == 0:
                e = (bb & cc) | (~bb & dd)
                j = i
            elif step == 1:
                e = (bb & dd) | (cc & ~dd)
                j = (i * 5 + 1) & 15
            elif step == 2:
                e = bb ^ cc ^ dd
                j = (i * 3 + 5) & 15
            else:
                e = cc ^ (bb | ~dd)
                j = (i * 7) & 15
            aa, bb, cc, dd = dd, bb + rotate_bits_left[int(S[i])](aa + e + K[i] + input[j]), bb, cc
        
        unroll[shuffle, 64]()

        self.buffer += SIMD[DType.uint32, 4](aa, bb, cc, dd)

@always_inline
fn md5_string(value: String) -> SIMD[DType.uint8, 16]:
    var ctx = Md5Context()
    ctx.update(value.unsafe_ptr(), len(value))
    return ctx^.finalize()


--- my_utils/__init__.mojo ---

from memory import memcmp
from pathlib import Path

fn int_cmp(a: UInt32, b: UInt32) -> Int:
    return int(a) - int(b)

fn int_cmp64(a: UInt64, b: UInt64) -> Int:
    return int(a) - int(b)

fn int_to_str(a: UInt32) -> String:
    return str(a)

fn int_to_str64(a: UInt64) -> String:
    return str(a)

fn cmp_strl(a: StringLiteral, b: StringLiteral) -> Int:
    var l = min(len(a), len(b))
    var p1 = a.unsafe_ptr()
    var p2 = b.unsafe_ptr()
    var diff = memcmp(p1, p2, l)

    return diff if diff != 0 else len(a) - len(b)

fn cmp_str(a: String, b: String) -> Int:
    var l = min(len(a), len(b))
    var p1 = a.unsafe_ptr()
    var p2 = b.unsafe_ptr()
    var diff = memcmp(p1, p2, l)

    return diff if diff != 0 else len(a) - len(b)

fn stsl(a: StringLiteral) -> String:
    return a

fn corpus1() raises -> List[String]:
    return String('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque orci urna, pretium et porta ac, porttitor sit amet sem. Fusce sagittis lorem neque, vitae sollicitudin elit suscipit et. In interdum convallis nisl in ornare. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aliquam erat volutpat. Morbi mollis iaculis lectus ac tincidunt. Fusce nisi lacus, semper eu dignissim et, malesuada non mi. Sed euismod urna vel elit faucibus, eu bibendum ante fringilla. Curabitur tempus in turpis at mattis. Aliquam erat volutpat. Donec maximus elementum felis, sit amet dignissim augue tincidunt blandit. Aliquam fermentum, est eu mollis.').split(" ")

fn corpus2() raises -> List[String]:
    return String('But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains. But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains.But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure? On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection:').split(" ")

fn corpus3() raises -> List[String]:
    return String('A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions! A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions! A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions!A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls').split(" ")

fn corpus4() raises -> List[String]:
    return String('Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое. Лежа на панцирнотвердой спине, он видел, стоило ему приподнять голову, свой коричневый, выпуклый, разделенный дугообразными чешуйками живот, на верхушке которого еле держалось готовое вот-вот окончательно сползти одеяло. Его многочисленные, убого тонкие по сравнению с остальным телом ножки беспомощно копошились у него перед глазами. «Что со мной случилось?» – подумал он. Это не было сном. Его комната, настоящая, разве что слишком маленькая, но обычная комната, мирно покоилась в своих четырех хорошо знакомых стенах. Над столом, где были разложены распакованные образцы сукон – Замза был коммивояжером, – висел портрет, который он недавно вырезал из иллюстрированного журнала и вставил в красивую золоченую рамку. На портрете была изображена дама в меховой шляпе и боа, она сидела очень прямо и протягивала зрителю тяжелую меховую муфту, в которой целиком исчезала ее рука. Затем взгляд Грегора устремился в окно, и пасмурная погода – слышно было, как по жести подоконника стучат капли дождя – привела его и вовсе в грустное настроение. «Хорошо бы еще немного поспать и забыть всю эту чепуху», – подумал он, но это было совершенно неосуществимо, он привык спать на правом боку, а в теперешнем своем состоянии он никак не мог принять этого положения. С какой бы силой ни поворачивался он на правый бок, он неизменно сваливался опять на спину. Закрыв глаза, чтобы не видеть своих барахтающихся ног, он проделал это добрую сотню раз и отказался от этих попыток только тогда, когда почувствовал какую-то неведомую дотоле, тупую и слабую боль в боку. «Ах ты, господи, – подумал он, – какую я выбрал хлопотную профессию! Изо дня в день в разъездах. Деловых волнений куда больше, чем на месте, в торговом доме, а кроме того, изволь терпеть тяготы дороги, думай о расписании поездов, мирись с плохим, нерегулярным питанием, завязывай со все новыми и новыми людьми недолгие, никогда не бывающие сердечными отношения. Черт бы побрал все это!» Он почувствовал вверху живота легкий зуд; медленно подвинулся на спине к прутьям кровати, чтобы удобнее было поднять голову; нашел зудевшее место, сплошь покрытое, как оказалось, белыми непонятными точечками; хотел было ощупать это место одной из ножек, но сразу отдернул ее, ибо даже простое прикосновение вызвало у него, Грегора, озноб. Он соскользнул в прежнее свое положение. «От этого раннего вставания, – подумал он, – можно совсем обезуметь. Человек должен высыпаться. Другие коммивояжеры живут, как одалиски. Когда я, например, среди дня возвращаюсь в гостиницу, чтобы переписать полученные заказы, эти господа только завтракают. А осмелься я вести себя так, мои хозяин выгнал бы меня сразу. Кто знает, впрочем, может быть, это было бы даже очень хорошо для меня. Если бы я не сдерживался ради родителей, я бы давно заявил об уходе, я бы подошел к своему хозяину и выложил ему все, что о нем думаю. Он бы так и свалился с конторки! Странная у него манера – садиться на конторку и с ее высоты разговаривать со служащим, который вдобавок вынужден подойти вплотную к конторке из-за того, что хозяин туг на ухо. Однако надежда еще не совсем потеряна: как только я накоплю денег, чтобы выплатить долг моих родителей – на это уйдет еще лет пять-шесть, – я так и поступлю. Тут-то мы и распрощаемся раз и навсегда. А пока что надо подниматься, мой поезд отходит в пять». И он взглянул на будильник, который тикал на сундуке. «Боже правый!» – подумал он. Было половина седьмого, и стрелки спокойно двигались дальше, было даже больше половины, без малого уже три четверти. Неужели будильник не звонил? С кровати было видно, что он поставлен правильно, на четыре часа; и он, несомненно, звонил. Но как можно было спокойно спать под этот сотрясающий мебель трезвон? Ну, спал-то он неспокойно, но, видимо, крепко. Однако что делать теперь? Следующий поезд уходит в семь часов; чтобы поспеть на него, он должен отчаянно торопиться, а набор образцов еще не упакован, да и сам он отнюдь не чувствует себя свежим и легким на подъем. И даже поспей он на поезд, хозяйского разноса ему все равно не избежать – ведь рассыльный торгового дома дежурил у пятичасового поезда и давно доложил о его, Грегора, опоздании. Рассыльный, человек бесхарактерный и неумный, был ставленником хозяина. А что, если сказаться больным? Но это было бы крайне неприятно и показалось бы подозрительным, ибо за пятилетнюю свою службу Грегор ни разу еще не болел. Хозяин, конечно, привел бы врача больничной кассы и стал попрекать родителей сыном-лентяем, отводя любые возражения ссылкой на этого врача, по мнению которого все люди на свете совершенно здоровы и только не любят работать. И разве в данном случае он был бы так уж неправ? Если не считать сонливости, действительно странной после такого долгого сна, Грегор и в самом деле чувствовал себя превосходно и был даже чертовски голоден.Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое. Лежа на панцирнотвердой спине, он видел, стоило ему приподнять голову, свой коричневый, выпуклый, разделенный дугообразными чешуйками живот, на верхушке которого еле держалось готовое вот-вот окончательно сползти одеяло. Его многочисленные, убого тонкие по сравнению с остальным телом ножки беспомощно копошились у него перед глазами. «Что со мной случилось?» – подумал он. Это не было сном. Его комната, настоящая, разве что слишком маленькая, но обычная комната, мирно покоилась в своих четырех хорошо знакомых стенах. Над столом, где были разложены распакованные образцы сукон – Замза был коммивояжером, – висел портрет, который он недавно вырезал из иллюстрированного журнала и вставил в красивую золоченую рамку. На портрете была изображена дама в меховой шляпе и боа, она сидела очень прямо и протягивала зрителю тяжелую меховую муфту, в которой целиком исчезала ее рука. Затем взгляд Грегора устремился в окно, и пасмурная погода – слышно было, как по жести подоконника стучат капли дождя – привела его и вовсе в грустное настроение. «Хорошо бы еще немного поспать и забыть всю эту чепуху», – подумал он, но это было совершенно неосуществимо, он привык спать на правом боку, а в теперешнем своем состоянии он никак не мог принять этого положения. С какой бы силой ни поворачивался он на правый бок, он неизменно сваливался опять на спину.').split(" ")

fn corpus5() raises -> List[String]:
    return String('Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort "und" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch. Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort "und" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch. Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort "und" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch.Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen').split(" ")

fn corpus6() raises -> List[String]:
    return String('米くを舵4物委らご氏松ハナテフ月関ソ時平ふいの博情れじフ牟万い元56園フメヤオ試図ロツヤ未備王こと傷喫羅踊んゆし。栃ユヱオ書著作ユソツロ英祉業ア大課ご権質フべ空8午キ切軟づン著郎そゃす格町採ヱオマコ処8付国ムハチア究表でなだ際無ロミヱ地兵ぴげ庭体すク発抜爆位や。楽富むゆず盛航カナセ携代ハ本高きた員59今骸ンラえぜ城解イケ穴訴ぽぎ属住ヤケトヌ抱点ト広注厚でて。 国リ出難セユメ軍手ヘカウ画形サヲシ猛85用ヲキミ心死よしと身処ケヨミオ教主ーぽ事業んく字国たさょ図能シミスヤ社8板ル岡世58次戒知院んれり。市メ誘根カ数問禁竹ゃれえみ給辺のでみき今二ぎさ裕止過こクすと無32郎所ラた生展ヌヘス成度慣葬勇厘ばてか。室ゃ下携疲ム色権がぽりっ銃週ノオ姫千テム健蔵い研手ッ放容ル告属め旅側26企サノヨ宅都福ぞ通待ちぴね種脳イど労希望義通むン。 罰しい続負せ著低たル異師ユハワ東添質コチ転集ルヤ雇聴約ヒ前統らた情厳ゆさでや真胸や有披暑棚豆ゆぼたけ。盛ワセロナ情競クるっわ講3音ずをせ少地めしぜょ手63明視れに判企ヒヌエソ求総58特本ね井比ユラキ禁頭馬るゅリす能率率かがさわ。葉サソ医郡ヱヘソ労帰ナケスミ救写ワヘ株審ネヒニミ安逮イ人画ラ涯車はラ極騒りなド件5級ンかふー劇41著ぱぐ凱討だ文世ぶづどま界善魅マ渓経競融れがや。 連ーぜらご模分ッ視外ばフく運発群ほぼづ育越一ほごクけ案募ヲイソ治会イせフ製君ぜた漢村1変リヒ構5際ツ御文ヲ臭入さドぼ代書ハケ引技ろみれ回観注倉徹ぱ。論ラづ海要サ情座ゃり齢宣ラモエ芸化エマホ覧催回ら戦69本外ト葬岳な政画か連針ぴリフず。約ル闘辺ぽ経2応掲ホサアラ塾小コラ画決クノオ上室レヌヱ勝逮ぜるえむ責豊チノ明意ひけ訟6碁草メタチエ財午召喝塊む。 決めでわ名金つけレわ続人県約ぽぼす尾腹ユサ戦載リシ護賀レモフツ重涯ニ治者むんっみ職更カタチレ提話2何ワ責東まけげふ能政ヌ供禁がびてわ提改倶れめ。読み担後ぽ安加ぎ論鹿ツ統最お気麻月つじもあ竜思いろめ判必満理トコ文連ムイウハ寄串ざほびー。文ゆこっ向27年メイ便能ノセヲ待1王スねたゆ伝派んね点過カト治読よにきべ使人スシ都言え阻8割べづえみ注引敷的岳犠眠どそ。 学用イだ医客開ロ供界もぞだ実隆モイヌ務坂ナコヲ権野ろづ初場ぱ低会づぱじ新倒コ化政レ止奮浸猪ッわえづ。形いやリ要帰ほまむだ業領スル必打さ島14巻リ集日ネヘホタ面幅ち写上そぴ円図ムタコモ報使イわざと会催ヤヲ康証をドぶレ盤岡ホハツ作29管しをめ公問懐蓄っさ。来ゆぼあぱ投秋シ語右ぐ身靖かば辛握捕家記ヘワ神岐囲づ毘観メテクツ政73夕罪57需93誌飲査仁さ。 変レめ束球よんま会特ヱコ聞重だ史純ーどる件32浦レぴよゃ上強ネラリロ査従セユヤ専棋光レ作表ひぶ予正ぜーな誉確フス函6報円ス進治ね能営済否雄でわょ。42生型ば着続ア短実ぎおめび前環闘ラヤヲル診均っとにの声公トヱテマ整試椅情久妊舌頃ざとっく。品キチトテ阿国ラら受87世ヲフセリ川86個ーょぼげ危子ヘレカメ無会ぱかへ事通んかて電条ロツ徴商ぶぞそを居暑メ害広せもがり禁応レミヲ応響割壮憶はぱ。 千れンが織財メニ況界ネトレミ学豊フオホシ近月レたやご的罪ょな菱技ちる警栗エセ提89林危氷48参ア説森クキヒヱ薬社ホコエリ負和ルび紀下ケミイ掲歳特ごず扱底ク護木連ちクを各形ばすか。変ぱなれ町7融ヌ街準以タユヘム質裕ぶで遺語俊ぎずょ事金文キ写多山ーゆに歩帯すで会世クぜよ論写ヲ達71林危氷5間続ぎぜび高怠す。 係8青け応著ミ戦条ナヘネカ思79未ぎ算伊をゃ泉人ーづ需説っ畑鹿27軽ラソツ権2促千護ルロナカ開国ケ暴嶋ご池表だ。佐フナ訪麻はてせば勝効をあ医戦画とさわぴ者両すいあ並来んば載食ぴ件友頂業へえぞ魚祝ネラ聞率スコリケ始全ンこび夫出ドふ今布うぎふゅ実克即哉循やしんな。 暮す備54依紀てッん末刊と柔称むてス無府ケイ変壌をぱ汁連フマス海世ヌ中負知問ナヘケ純推ひ読着ヒ言若私軽れ。掲けフむ王本オコ線人をっさ必和断セソヲハ図芸ちかな防長りぶは投新意相ツ並5余セ職岳ぞ端古空援そ。森ヨエチ題5東っ自兄ち暴5近鹿横ト的京ハ安氷ナキ深際ぎ並節くスむの権工ほルせ京49効タムチ処三ぞぴラ済国ずっ文経ヘトミ水分準そが。').split(" ")

fn corpus7() raises -> List[String]:
    return String('AbortMultipartUpload CompleteMultipartUpload CopyObject CreateBucket CreateMultipartUpload DeleteBucket DeleteBucketAnalyticsConfiguration DeleteBucketCors DeleteBucketEncryption DeleteBucketIntelligentTieringConfiguration DeleteBucketInventoryConfiguration DeleteBucketLifecycle DeleteBucketMetricsConfiguration DeleteBucketOwnershipControls DeleteBucketPolicy DeleteBucketReplication DeleteBucketTagging DeleteBucketWebsite DeleteObject DeleteObjects DeleteObjectTagging DeletePublicAccessBlock GetBucketAccelerateConfiguration GetBucketAcl GetBucketAnalyticsConfiguration GetBucketCors GetBucketEncryption GetBucketIntelligentTieringConfiguration GetBucketInventoryConfiguration GetBucketLifecycle GetBucketLifecycleConfiguration GetBucketLocation GetBucketLogging GetBucketMetricsConfiguration GetBucketNotification GetBucketNotificationConfiguration GetBucketOwnershipControls GetBucketPolicy GetBucketPolicyStatus GetBucketReplication GetBucketRequestPayment GetBucketTagging GetBucketVersioning GetBucketWebsite GetObject GetObjectAcl GetObjectAttributes GetObjectLegalHold GetObjectLockConfiguration GetObjectRetention GetObjectTagging GetObjectTorrent GetPublicAccessBlock HeadBucket HeadObject ListBucketAnalyticsConfigurations ListBucketIntelligentTieringConfigurations ListBucketInventoryConfigurations ListBucketMetricsConfigurations ListBuckets ListMultipartUploads ListObjects ListObjectsV2 ListObjectVersions ListParts PutBucketAccelerateConfiguration PutBucketAcl PutBucketAnalyticsConfiguration PutBucketCors PutBucketEncryption PutBucketIntelligentTieringConfiguration PutBucketInventoryConfiguration PutBucketLifecycle PutBucketLifecycleConfiguration PutBucketLogging PutBucketMetricsConfiguration PutBucketNotification PutBucketNotificationConfiguration PutBucketOwnershipControls PutBucketPolicy PutBucketReplication PutBucketRequestPayment PutBucketTagging PutBucketVersioning PutBucketWebsite PutObject PutObjectAcl PutObjectLegalHold PutObjectLockConfiguration PutObjectRetention PutObjectTagging PutPublicAccessBlock RestoreObject SelectObjectContent UploadPart UploadPartCopy WriteGetObjectResponse", "CreateAccessPoint CreateAccessPointForObjectLambda CreateBucket CreateJob CreateMultiRegionAccessPoint DeleteAccessPoint DeleteAccessPointForObjectLambda DeleteAccessPointPolicy DeleteAccessPointPolicyForObjectLambda DeleteBucket DeleteBucketLifecycleConfiguration DeleteBucketPolicy DeleteBucketReplication DeleteBucketTagging DeleteJobTagging DeleteMultiRegionAccessPoint DeletePublicAccessBlock DeleteStorageLensConfiguration DeleteStorageLensConfigurationTagging DescribeJob DescribeMultiRegionAccessPointOperation GetAccessPoint GetAccessPointConfigurationForObjectLambda GetAccessPointForObjectLambda GetAccessPointPolicy GetAccessPointPolicyForObjectLambda GetAccessPointPolicyStatus GetAccessPointPolicyStatusForObjectLambda GetBucket GetBucketLifecycleConfiguration GetBucketPolicy GetBucketReplication GetBucketTagging GetBucketVersioning GetJobTagging GetMultiRegionAccessPoint GetMultiRegionAccessPointPolicy GetMultiRegionAccessPointPolicyStatus GetMultiRegionAccessPointRoutes GetPublicAccessBlock GetStorageLensConfiguration GetStorageLensConfigurationTagging ListAccessPoints ListAccessPointsForObjectLambda ListJobs ListMultiRegionAccessPoints ListRegionalBuckets ListStorageLensConfigurations PutAccessPointConfigurationForObjectLambda PutAccessPointPolicy PutAccessPointPolicyForObjectLambda PutBucketLifecycleConfiguration PutBucketPolicy PutBucketReplication PutBucketTagging PutBucketVersioning PutJobTagging PutMultiRegionAccessPointPolicy PutPublicAccessBlock PutStorageLensConfiguration PutStorageLensConfigurationTagging SubmitMultiRegionAccessPointRoutes UpdateJobPriority UpdateJobStatus').split(" ")

fn corpus8() raises -> List[String]:
    var text = Path("/usr/share/dict/words").read_text()
    print("Text:", len(text))
    return text.splitlines()

--- sha/__init__.mojo ---

from .sha256 import sha256_encode

--- sha/sha256.mojo ---

from memory import memcpy
from collections.vector import InlinedFixedVector
import time

@always_inline
fn big_endian_bytes_to_dword(
	first: UInt8, second: UInt8, third: UInt8, fourth: UInt8
) -> UInt32:
	var a = first.cast[DType.uint32]() << 24
	var b = second.cast[DType.uint32]() << 16
	var c = third.cast[DType.uint32]() << 8
	var d = fourth.cast[DType.uint32]() << 0
	return a | b | c | d


@always_inline
fn big_endian_dword_to_bytes(word: UInt32) -> InlinedFixedVector[UInt8, 4]:
	var v = InlinedFixedVector[UInt8, 4](4)
	var a = (word >> 24) & 255
	var b = (word >> 16) & 255
	var c = (word >> 8) & 255
	var d = word & 255
	v.append(a.cast[DType.uint8]())
	v.append(b.cast[DType.uint8]())
	v.append(c.cast[DType.uint8]())
	v.append(d.cast[DType.uint8]())
	return v


@always_inline
fn big_endian_qword_to_bytes(word: UInt64) -> InlinedFixedVector[UInt8, 8]:
	var v = InlinedFixedVector[UInt8, 8](8)
	var a = (word >> 56) & 255
	var b = (word >> 48) & 255
	var c = (word >> 40) & 255
	var d = (word >> 32) & 255
	var e = (word >> 24) & 255
	var f = (word >> 16) & 255
	var g = (word >> 8) & 255
	var h = word & 255
	v.append(a.cast[DType.uint8]())
	v.append(b.cast[DType.uint8]())
	v.append(c.cast[DType.uint8]())
	v.append(d.cast[DType.uint8]())
	v.append(e.cast[DType.uint8]())
	v.append(f.cast[DType.uint8]())
	v.append(g.cast[DType.uint8]())
	v.append(h.cast[DType.uint8]())
	return v


# bit rotate right
@always_inline
fn bitrr(integer: UInt32, rotations: UInt32) -> UInt32:
	return (integer >> rotations) | (integer << (32 - rotations))


alias k = SIMD[DType.uint32, 64](
	0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
	0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
	0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
	0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
	0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 
	0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 
	0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 
	0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2	
)

alias h = SIMD[DType.uint32, 8](
	0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19,
)

# for reference see https://en.wikipedia.org/wiki/SHA-2#Pseudocode
# right now it internally copies the byte_view into a dynamic vector and works on that
# this is slow, but i don't have the mojo mojo to chunk it out for zero-copy
fn sha256_encode(byte_view: UnsafePointer[UInt8], length: Int) -> InlinedFixedVector[UInt8, 32]:

	var h0: UInt32 = 0x6A09E667
	var h1: UInt32 = 0xBB67AE85
	var h2: UInt32 = 0x3C6EF372
	var h3: UInt32 = 0xA54FF53A
	var h4: UInt32 = 0x510E527F
	var h5: UInt32 = 0x9B05688C
	var h6: UInt32 = 0x1F83D9AB
	var h7: UInt32 = 0x5BE0CD19

	var one_bit: UInt8 = 0b1000_0000

	var exact_chunks = length // 64
	var remainder_start = exact_chunks * 64
	var remainder_length = length % 64
	var bare_min_extra_bytes = remainder_length + 9
	var extra_space = InlinedFixedVector[UInt8,128](128)
	for i in range(remainder_length):
		extra_space.append(byte_view[remainder_start + i])

	extra_space.append(one_bit)
	var only_one_chunk_needed = bare_min_extra_bytes <= 64
	var tail_bytes = big_endian_qword_to_bytes(length * 8)
	if only_one_chunk_needed:
		while 8+extra_space.current_size < 64:
			extra_space.append(0)
	else:
		while 8+extra_space.current_size < 128:
			extra_space.append(0)
	
	for i in range(8):
		extra_space.append(tail_bytes[i])


	var w = InlinedFixedVector[UInt32, 64](64)
		# 	(The initial values in w[0..63] don't matter, so many implementations zero them here)
	for i in range(64):
		w.append(0)


	# loop through the full sets of 64 from the byte view
	# later, a little code duplication to repeat on the extra space
	for chunk_number in range(exact_chunks):
		# 	create a 64-entry message schedule array w[0..63] of 32-bit words
		
		# 	copy chunk into first 16 words w[0..15] of the message schedule array
		@parameter
		for dword_i in range(16):
			var start_byte_within_chunk = dword_i * 4
			var start_byte_overall = start_byte_within_chunk + (64 * chunk_number)
			var i = start_byte_overall
			var dword = big_endian_bytes_to_dword(
				byte_view[i],
				byte_view[i + 1],
				byte_view[i + 2],
				byte_view[i + 3],
			)
			w[dword_i] = dword

		# Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array:
		@parameter
		for i in range(16, 64):
			# s0 := (w[i-15] rightrotate  7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift  3)
			var s0 = bitrr(w[i - 15], 7) ^ bitrr(w[i - 15], 18) ^ (w[i - 15] >> 3)
			# s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10)
			var s1 = bitrr(w[i - 2], 17) ^ bitrr(w[i - 2], 19) ^ (w[i - 2] >> 10)
			# w[i] := w[i-16] + s0 + w[i-7] + s1
			w[i] = w[i - 16] + s0 + w[i - 7] + s1

		var a = h0
		var b = h1
		var c = h2
		var d = h3
		var e = h4
		var f = h5
		var g = h6
		var h = h7

		@parameter
		for i in range(64):
			# S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)
			var S1 = bitrr(e, 6) ^ bitrr(e, 11) ^ bitrr(e, 25)
			# ch := (e and f) xor ((not e) and g)
			var ch = (e & f) ^ ((e ^ (0-1)) & g)
			# temp1 := h + S1 + ch + k[i] + w[i]
			var temp1 = h + S1 + ch + k[i] + w[i]
			# S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)
			var S0 = bitrr(a, 2) ^ bitrr(a, 13) ^ bitrr(a, 22)
			# maj := (a and b) xor (a and c) xor (b and c)
			var maj = (a & b) ^ (a & c) ^ (b & c)
			# temp2 := S0 + maj
			var temp2 = S0 + maj

			h = g
			g = f
			f = e
			e = d + temp1
			d = c
			c = b
			b = a
			a = temp1 + temp2

		h0 = h0 + a
		h1 = h1 + b
		h2 = h2 + c
		h3 = h3 + d
		h4 = h4 + e
		h5 = h5 + f
		h6 = h6 + g
		h7 = h7 + h

	#continue through the extra space
	var extra_chunks = extra_space.current_size // 64
	for chunk_number in range(extra_chunks):
		# 	create a 64-entry message schedule array w[0..63] of 32-bit words
		
		# 	copy chunk into first 16 words w[0..15] of the message schedule array
		@parameter
		for dword_i in range(16):
			var start_byte_within_chunk = dword_i * 4
			var start_byte_overall = start_byte_within_chunk + (64 * chunk_number)
			var i = start_byte_overall
			var dword = big_endian_bytes_to_dword(
				extra_space[i],
				extra_space[i + 1],
				extra_space[i + 2],
				extra_space[i + 3],
			)
			w[dword_i] = dword

		# Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array:
		@parameter
		for i in range(16, 64):
			# s0 := (w[i-15] rightrotate  7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift  3)
			var s0 = bitrr(w[i - 15], 7) ^ bitrr(w[i - 15], 18) ^ (w[i - 15] >> 3)
			# s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10)
			var s1 = bitrr(w[i - 2], 17) ^ bitrr(w[i - 2], 19) ^ (w[i - 2] >> 10)
			# w[i] := w[i-16] + s0 + w[i-7] + s1
			w[i] = w[i - 16] + s0 + w[i - 7] + s1

		var a = h0
		var b = h1
		var c = h2
		var d = h3
		var e = h4
		var f = h5
		var g = h6
		var h = h7

		@parameter
		for i in range(64):
			# S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)
			var S1 = bitrr(e, 6) ^ bitrr(e, 11) ^ bitrr(e, 25)
			# ch := (e and f) xor ((not e) and g)
			var ch = (e & f) ^ ((e ^ (0-1)) & g)
			# temp1 := h + S1 + ch + k[i] + w[i]
			var temp1 = h + S1 + ch + k[i] + w[i]
			# S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)
			var S0 = bitrr(a, 2) ^ bitrr(a, 13) ^ bitrr(a, 22)
			# maj := (a and b) xor (a and c) xor (b and c)
			var maj = (a & b) ^ (a & c) ^ (b & c)
			# temp2 := S0 + maj
			var temp2 = S0 + maj

			h = g
			g = f
			f = e
			e = d + temp1
			d = c
			c = b
			b = a
			a = temp1 + temp2

		h0 = h0 + a
		h1 = h1 + b
		h2 = h2 + c
		h3 = h3 + d
		h4 = h4 + e
		h5 = h5 + f
		h6 = h6 + g
		h7 = h7 + h



	var output = InlinedFixedVector[UInt8, 32](32)

	var digest_part_h0 = big_endian_dword_to_bytes(h0)
	for i in range(4):
		output.append(digest_part_h0[i])
	var digest_part_h1 = big_endian_dword_to_bytes(h1)
	for i in range(4):
		output.append(digest_part_h1[i])
	var digest_part_h2 = big_endian_dword_to_bytes(h2)
	for i in range(4):
		output.append(digest_part_h2[i])
	var digest_part_h3 = big_endian_dword_to_bytes(h3)
	for i in range(4):
		output.append(digest_part_h3[i])
	var digest_part_h4 = big_endian_dword_to_bytes(h4)
	for i in range(4):
		output.append(digest_part_h4[i])
	var digest_part_h5 = big_endian_dword_to_bytes(h5)
	for i in range(4):
		output.append(digest_part_h5[i])
	var digest_part_h6 = big_endian_dword_to_bytes(h6)
	for i in range(4):
		output.append(digest_part_h6[i])
	var digest_part_h7 = big_endian_dword_to_bytes(h7)
	for i in range(4):
		output.append(digest_part_h7[i])

	return output


--- sha/sha256_2.mojo ---



--- test_md5.mojo ---

from md5 import md5_string
from testing import assert_equal
from wyhasher import wyhash
from wyhasher.wyhasher import wymum

alias alphabete: String = "0123456789abcdef"

fn to_hex(v: SIMD[DType.uint8, 16]) -> String:
    var result: String = ""
    for i in range(16):
        var h = v[i] >> 4
        var l = v[i] & 15
        result += alphabete[int(h)]
        result += alphabete[int(l)]
    return result

fn main() raises:
    var a: String = "Hello 🔥"
    assert_equal(to_hex(md5_string(a)), "b9735ea236e0d3103a39ad102a2e990f")
    _ = a
    var b: String = '米くを舵4物委らご氏松ハナテフ月関ソ時平ふいの博情れじフ牟万い元56園フメヤオ試図ロツヤ未備王こと傷喫羅踊んゆし。栃ユヱオ書著作ユソツロ英祉業ア大課ご権質フべ空8午キ切軟づン著郎そゃす格町採ヱオマコ処8付国ムハチア究表でなだ際無ロミヱ地兵ぴげ庭体すク発抜爆位や。楽富むゆず盛航カナセ携代ハ本高きた員59今骸ンラえぜ城解イケ穴訴ぽぎ属住ヤケトヌ抱点ト広注厚でて。 国リ出難セユメ軍手ヘカウ画形サヲシ猛85用ヲキミ心死よしと身処ケヨミオ教主ーぽ事業んく字国たさょ図能シミスヤ社8板ル岡世58次戒知院んれり。市メ誘根カ数問禁竹ゃれえみ給辺のでみき今二ぎさ裕止過こクすと無32郎所ラた生展ヌヘス成度慣葬勇厘ばてか。室ゃ下携疲ム色権がぽりっ銃週ノオ姫千テム健蔵い研手ッ放容ル告属め旅側26企サノヨ宅都福ぞ通待ちぴね種脳イど労希望義通むン。 罰しい続負せ著低たル異師ユハワ東添質コチ転集ルヤ雇聴約ヒ前統らた情厳ゆさでや真胸や有披暑棚豆ゆぼたけ。盛ワセロナ情競クるっわ講3音ずをせ少地めしぜょ手63明視れに判企ヒヌエソ求総58特本ね井比ユラキ禁頭馬るゅリす能率率かがさわ。葉サソ医郡ヱヘソ労帰ナケスミ救写ワヘ株審ネヒニミ安逮イ人画ラ涯車はラ極騒りなド件5級ンかふー劇41著ぱぐ凱討だ文世ぶづどま界善魅マ渓経競融れがや。 連ーぜらご模分ッ視外ばフく運発群ほぼづ育越一ほごクけ案募ヲイソ治会イせフ製君ぜた漢村1変リヒ構5際ツ御文ヲ臭入さドぼ代書ハケ引技ろみれ回観注倉徹ぱ。論ラづ海要サ情座ゃり齢宣ラモエ芸化エマホ覧催回ら戦69本外ト葬岳な政画か連針ぴリフず。約ル闘辺ぽ経2応掲ホサアラ塾小コラ画決クノオ上室レヌヱ勝逮ぜるえむ責豊チノ明意ひけ訟6碁草メタチエ財午召喝塊む。 決めでわ名金つけレわ続人県約ぽぼす尾腹ユサ戦載リシ護賀レモフツ重涯ニ治者むんっみ職更カタチレ提話2何ワ責東まけげふ能政ヌ供禁がびてわ提改倶れめ。読み担後ぽ安加ぎ論鹿ツ統最お気麻月つじもあ竜思いろめ判必満理トコ文連ムイウハ寄串ざほびー。文ゆこっ向27年メイ便能ノセヲ待1王スねたゆ伝派んね点過カト治読よにきべ使人スシ都言え阻8割べづえみ注引敷的岳犠眠どそ。 学用イだ医客開ロ供界もぞだ実隆モイヌ務坂ナコヲ権野ろづ初場ぱ低会づぱじ新倒コ化政レ止奮浸猪ッわえづ。形いやリ要帰ほまむだ業領スル必打さ島14巻リ集日ネヘホタ面幅ち写上そぴ円図ムタコモ報使イわざと会催ヤヲ康証をドぶレ盤岡ホハツ作29管しをめ公問懐蓄っさ。来ゆぼあぱ投秋シ語右ぐ身靖かば辛握捕家記ヘワ神岐囲づ毘観メテクツ政73夕罪57需93誌飲査仁さ。 変レめ束球よんま会特ヱコ聞重だ史純ーどる件32浦レぴよゃ上強ネラリロ査従セユヤ専棋光レ作表ひぶ予正ぜーな誉確フス函6報円ス進治ね能営済否雄でわょ。42生型ば着続ア短実ぎおめび前環闘ラヤヲル診均っとにの声公トヱテマ整試椅情久妊舌頃ざとっく。品キチトテ阿国ラら受87世ヲフセリ川86個ーょぼげ危子ヘレカメ無会ぱかへ事通んかて電条ロツ徴商ぶぞそを居暑メ害広せもがり禁応レミヲ応響割壮憶はぱ。 千れンが織財メニ況界ネトレミ学豊フオホシ近月レたやご的罪ょな菱技ちる警栗エセ提89林危氷48参ア説森クキヒヱ薬社ホコエリ負和ルび紀下ケミイ掲歳特ごず扱底ク護木連ちクを各形ばすか。変ぱなれ町7融ヌ街準以タユヘム質裕ぶで遺語俊ぎずょ事金文キ写多山ーゆに歩帯すで会世クぜよ論写ヲ達71林危氷5間続ぎぜび高怠す。 係8青け応著ミ戦条ナヘネカ思79未ぎ算伊をゃ泉人ーづ需説っ畑鹿27軽ラソツ権2促千護ルロナカ開国ケ暴嶋ご池表だ。佐フナ訪麻はてせば勝効をあ医戦画とさわぴ者両すいあ並来んば載食ぴ件友頂業へえぞ魚祝ネラ聞率スコリケ始全ンこび夫出ドふ今布うぎふゅ実克即哉循やしんな。 暮す備54依紀てッん末刊と柔称むてス無府ケイ変壌をぱ汁連フマス海世ヌ中負知問ナヘケ純推ひ読着ヒ言若私軽れ。掲けフむ王本オコ線人をっさ必和断セソヲハ図芸ちかな防長りぶは投新意相ツ並5余セ職岳ぞ端古空援そ。森ヨエチ題5東っ自兄ち暴5近鹿横ト的京ハ安氷ナキ深際ぎ並節くスむの権工ほルせ京49効タムチ処三ぞぴラ済国ずっ文経ヘトミ水分準そが。'
    assert_equal(to_hex(md5_string(b)), "168f7f85febeb19dbad38502499ea1d0")
    _ = b


--- test_sha256.mojo ---

# import time
# from sha import sha256_encode
# from testing import assert_equal
# from collections.vector import InlinedFixedVector

# fn print_hex(digest: InlinedFixedVector[UInt8, 32]):
#     var lookup = String("0123456789abcdef")
#     var result: String = ""
#     for i in range(len(digest)):
#         var v = digest[i].to_int()
#         result += lookup[(v >> 4)]
#         result += lookup[v & 15]
    
#     print(result)
#     print(len(digest))
#     print(len("b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"))
#     print(len("985752100505598575751521005110148569753501015350100551009755100979810297995256521011021015155975351564810110157485656102559799101501011029910010157"))

# fn main():
#     var bytes = 1024 * 1024 * 256 + 78
#     var bytes_to_hash: DynamicVector[UInt8] = kinda_random_bytes(bytes)
#     var ptr = DTypePointer[DType.uint8](bytes_to_hash.data.value)
#     var buffer = Buffer[DType.uint8](ptr, bytes_to_hash.size)
#     var before = time.now()
#     var hash = sha256_encode(ptr, bytes)
#     var after = time.now()
#     var keep_vector_alive = bytes_to_hash[4]
#     var ns = after - before
#     var seconds = ns / 1_000_000_000
#     var megabytes = bytes / 1_000_000
#     for i in range(hash.size):
#         print(hash[i])
#     print("megabytes per second")
#     print(megabytes / seconds)
#     var text = "hello world"
#     print(text)
#     print_hex(sha256_encode(text.data().bitcast[DType.uint8](), len(text)))


# fn kinda_random_bytes(length: Int) -> DynamicVector[UInt8]:
# 	var vec = DynamicVector[UInt8](capacity=length)
# 	var n: UInt8 = 245
# 	var cycle: UInt8 = 1
# 	for i in range(length):
# 		var shifted = n >> 3
# 		var shiftalso = n << 4
# 		var more = shifted ^ n ^ shiftalso
# 		var next = n + more
# 		n = next
# 		cycle ^= n
# 		vec.append(n + cycle)

# 	return vec

--- test_sha256_2.mojo ---

# from memory import memcpy
# from collections.vector import InlinedFixedVector
# import time


# fn main():
# 	var bytes = 1024 * 1024 * 256 + 78
# 	var bytes_to_hash: List[UInt8] = kinda_random_bytes(bytes)
# 	var ptr = bytes_to_hash.unsafe_ptr()
# 	var buffer = Buffer[DType.uint8](ptr, bytes_to_hash.size)
# 	var before = time.now()
# 	var hash = sha256(buffer)
# 	var after = time.now()
# 	var keep_vector_alive = bytes_to_hash[4]
# 	var ns = after - before
# 	var seconds = ns / 1_000_000_000
# 	var megabytes = bytes / 1_000_000
# 	for i in range(hash.size):
# 		print(hash[i])
# 	print("megabytes per second")
# 	print(megabytes / seconds)


# fn kinda_random_bytes(length: Int) -> DynamicVector[UInt8]:
# 	var vec = DynamicVector[UInt8](capacity=length)
# 	var n: UInt8 = 245
# 	var cycle: UInt8 = 1
# 	for i in range(length):
# 		var shifted = n >> 3
# 		var shiftalso = n << 4
# 		var more = shifted ^ n ^ shiftalso
# 		var next = n + more
# 		n = next
# 		cycle ^= n
# 		vec.append(n + cycle)

# 	return vec


# @always_inline
# fn big_endian_bytes_to_dword(
# 	first: UInt8, second: UInt8, third: UInt8, fourth: UInt8
# ) -> UInt32:
# 	var a = first.cast[DType.uint32]() << 24
# 	var b = second.cast[DType.uint32]() << 16
# 	var c = third.cast[DType.uint32]() << 8
# 	var d = fourth.cast[DType.uint32]() << 0
# 	return a | b | c | d


# @always_inline
# fn big_endian_dword_to_bytes(word: UInt32) -> InlinedFixedVector[UInt8, 4]:
# 	var v = InlinedFixedVector[UInt8, 4](4)
# 	var a = (word >> 24) & 255
# 	var b = (word >> 16) & 255
# 	var c = (word >> 8) & 255
# 	var d = word & 255
# 	v.append(a.cast[DType.uint8]())
# 	v.append(b.cast[DType.uint8]())
# 	v.append(c.cast[DType.uint8]())
# 	v.append(d.cast[DType.uint8]())
# 	return v


# @always_inline
# fn big_endian_qword_to_bytes(word: UInt64) -> InlinedFixedVector[UInt8, 8]:
# 	var v = InlinedFixedVector[UInt8, 8](8)
# 	var a = (word >> 56) & 255
# 	var b = (word >> 48) & 255
# 	var c = (word >> 40) & 255
# 	var d = (word >> 32) & 255
# 	var e = (word >> 24) & 255
# 	var f = (word >> 16) & 255
# 	var g = (word >> 8) & 255
# 	var h = word & 255
# 	v.append(a.cast[DType.uint8]())
# 	v.append(b.cast[DType.uint8]())
# 	v.append(c.cast[DType.uint8]())
# 	v.append(d.cast[DType.uint8]())
# 	v.append(e.cast[DType.uint8]())
# 	v.append(f.cast[DType.uint8]())
# 	v.append(g.cast[DType.uint8]())
# 	v.append(h.cast[DType.uint8]())
# 	return v


# # bit rotate right
# @always_inline
# fn bitrr(integer: UInt32, rotations: UInt32) -> UInt32:
# 	return (integer >> rotations) | (integer << (32 - rotations))


# # for reference see https://en.wikipedia.org/wiki/SHA-2#Pseudocode
# # right now it internally copies the byte_view into a dynamic vector and works on that
# # this is slow, but i don't have the mojo mojo to chunk it out for zero-copy
# fn sha256(byte_view: Buffer[_, DType.uint8, 0]) -> InlinedFixedVector[UInt8, 32]:
# 	var k = InlinedFixedVector[UInt32, 64](64)
# 	k.append(0x428A2F98)
# 	k.append(0x71374491)
# 	k.append(0xB5C0FBCF)
# 	k.append(0xE9B5DBA5)
# 	k.append(0x3956C25B)
# 	k.append(0x59F111F1)
# 	k.append(0x923F82A4)
# 	k.append(0xAB1C5ED5)
# 	k.append(0xD807AA98)
# 	k.append(0x12835B01)
# 	k.append(0x243185BE)
# 	k.append(0x550C7DC3)
# 	k.append(0x72BE5D74)
# 	k.append(0x80DEB1FE)
# 	k.append(0x9BDC06A7)
# 	k.append(0xC19BF174)
# 	k.append(0xE49B69C1)
# 	k.append(0xEFBE4786)
# 	k.append(0x0FC19DC6)
# 	k.append(0x240CA1CC)
# 	k.append(0x2DE92C6F)
# 	k.append(0x4A7484AA)
# 	k.append(0x5CB0A9DC)
# 	k.append(0x76F988DA)
# 	k.append(0x983E5152)
# 	k.append(0xA831C66D)
# 	k.append(0xB00327C8)
# 	k.append(0xBF597FC7)
# 	k.append(0xC6E00BF3)
# 	k.append(0xD5A79147)
# 	k.append(0x06CA6351)
# 	k.append(0x14292967)
# 	k.append(0x27B70A85)
# 	k.append(0x2E1B2138)
# 	k.append(0x4D2C6DFC)
# 	k.append(0x53380D13)
# 	k.append(0x650A7354)
# 	k.append(0x766A0ABB)
# 	k.append(0x81C2C92E)
# 	k.append(0x92722C85)
# 	k.append(0xA2BFE8A1)
# 	k.append(0xA81A664B)
# 	k.append(0xC24B8B70)
# 	k.append(0xC76C51A3)
# 	k.append(0xD192E819)
# 	k.append(0xD6990624)
# 	k.append(0xF40E3585)
# 	k.append(0x106AA070)
# 	k.append(0x19A4C116)
# 	k.append(0x1E376C08)
# 	k.append(0x2748774C)
# 	k.append(0x34B0BCB5)
# 	k.append(0x391C0CB3)
# 	k.append(0x4ED8AA4A)
# 	k.append(0x5B9CCA4F)
# 	k.append(0x682E6FF3)
# 	k.append(0x748F82EE)
# 	k.append(0x78A5636F)
# 	k.append(0x84C87814)
# 	k.append(0x8CC70208)
# 	k.append(0x90BEFFFA)
# 	k.append(0xA4506CEB)
# 	k.append(0xBEF9A3F7)
# 	k.append(0xC67178F2)

# 	var h0: UInt32 = 0x6A09E667
# 	var h1: UInt32 = 0xBB67AE85
# 	var h2: UInt32 = 0x3C6EF372
# 	var h3: UInt32 = 0xA54FF53A
# 	var h4: UInt32 = 0x510E527F
# 	var h5: UInt32 = 0x9B05688C
# 	var h6: UInt32 = 0x1F83D9AB
# 	var h7: UInt32 = 0x5BE0CD19

# 	var one_bit: UInt8 = 0b1000_0000

# 	var exact_chunks = byte_view.dynamic_size // 64
# 	var remainder_start = exact_chunks * 64
# 	var remainder_length = byte_view.dynamic_size % 64
# 	var bare_min_extra_bytes = remainder_length + 9
# 	var extra_space = InlinedFixedVector[UInt8,128](128)
# 	for i in range(remainder_length):
# 		extra_space.append(byte_view[remainder_start + i])

# 	extra_space.append(one_bit)
# 	var only_one_chunk_needed = bare_min_extra_bytes <= 64
# 	var tail_bytes = big_endian_qword_to_bytes(byte_view.dynamic_size * 8)
# 	if only_one_chunk_needed:
# 		while 8+extra_space.current_size < 64:
# 			extra_space.append(0)
# 	else:
# 		while 8+extra_space.current_size < 128:
# 			extra_space.append(0)
	
# 	for i in range(8):
# 		extra_space.append(tail_bytes[i])


# 	var w = InlinedFixedVector[UInt32, 64](64)
# 		# 	(The initial values in w[0..63] don't matter, so many implementations zero them here)
# 	for i in range(64):
# 		w.append(0)


# 	# loop through the full sets of 64 from the byte view
# 	# later, a little code duplication to repeat on the extra space
# 	for chunk_number in range(exact_chunks):
# 		# 	create a 64-entry message schedule array w[0..63] of 32-bit words
		
# 		# 	copy chunk into first 16 words w[0..15] of the message schedule array
# 		@unroll
# 		for dword_i in range(16):
# 			var start_byte_within_chunk = dword_i * 4
# 			var start_byte_overall = start_byte_within_chunk + (64 * chunk_number)
# 			var i = start_byte_overall
# 			var dword = big_endian_bytes_to_dword(
# 				byte_view[i],
# 				byte_view[i + 1],
# 				byte_view[i + 2],
# 				byte_view[i + 3],
# 			)
# 			w[dword_i] = dword

# 		# Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array:
# 		@unroll
# 		for i in range(16, 64):
# 			# s0 := (w[i-15] rightrotate  7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift  3)
# 			var s0 = bitrr(w[i - 15], 7) ^ bitrr(w[i - 15], 18) ^ (w[i - 15] >> 3)
# 			# s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10)
# 			var s1 = bitrr(w[i - 2], 17) ^ bitrr(w[i - 2], 19) ^ (w[i - 2] >> 10)
# 			# w[i] := w[i-16] + s0 + w[i-7] + s1
# 			w[i] = w[i - 16] + s0 + w[i - 7] + s1

# 		var a = h0
# 		var b = h1
# 		var c = h2
# 		var d = h3
# 		var e = h4
# 		var f = h5
# 		var g = h6
# 		var h = h7

# 		@unroll
# 		for i in range(64):
# 			# S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)
# 			var S1 = bitrr(e, 6) ^ bitrr(e, 11) ^ bitrr(e, 25)
# 			# ch := (e and f) xor ((not e) and g)
# 			var ch = (e & f) ^ ((e ^ (0-1)) & g)
# 			# temp1 := h + S1 + ch + k[i] + w[i]
# 			var temp1 = h + S1 + ch + k[i] + w[i]
# 			# S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)
# 			var S0 = bitrr(a, 2) ^ bitrr(a, 13) ^ bitrr(a, 22)
# 			# maj := (a and b) xor (a and c) xor (b and c)
# 			var maj = (a & b) ^ (a & c) ^ (b & c)
# 			# temp2 := S0 + maj
# 			var temp2 = S0 + maj

# 			h = g
# 			g = f
# 			f = e
# 			e = d + temp1
# 			d = c
# 			c = b
# 			b = a
# 			a = temp1 + temp2

# 		h0 = h0 + a
# 		h1 = h1 + b
# 		h2 = h2 + c
# 		h3 = h3 + d
# 		h4 = h4 + e
# 		h5 = h5 + f
# 		h6 = h6 + g
# 		h7 = h7 + h

# 	#continue through the extra space
# 	var extra_chunks = extra_space.current_size // 64
# 	for chunk_number in range(extra_chunks):
# 		# 	create a 64-entry message schedule array w[0..63] of 32-bit words
		
# 		# 	copy chunk into first 16 words w[0..15] of the message schedule array
# 		@unroll
# 		for dword_i in range(16):
# 			var start_byte_within_chunk = dword_i * 4
# 			var start_byte_overall = start_byte_within_chunk + (64 * chunk_number)
# 			var i = start_byte_overall
# 			var dword = big_endian_bytes_to_dword(
# 				extra_space[i],
# 				extra_space[i + 1],
# 				extra_space[i + 2],
# 				extra_space[i + 3],
# 			)
# 			w[dword_i] = dword

# 		# Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array:
# 		@unroll
# 		for i in range(16, 64):
# 			# s0 := (w[i-15] rightrotate  7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift  3)
# 			var s0 = bitrr(w[i - 15], 7) ^ bitrr(w[i - 15], 18) ^ (w[i - 15] >> 3)
# 			# s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10)
# 			var s1 = bitrr(w[i - 2], 17) ^ bitrr(w[i - 2], 19) ^ (w[i - 2] >> 10)
# 			# w[i] := w[i-16] + s0 + w[i-7] + s1
# 			w[i] = w[i - 16] + s0 + w[i - 7] + s1

# 		var a = h0
# 		var b = h1
# 		var c = h2
# 		var d = h3
# 		var e = h4
# 		var f = h5
# 		var g = h6
# 		var h = h7

# 		@unroll
# 		for i in range(64):
# 			# S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)
# 			var S1 = bitrr(e, 6) ^ bitrr(e, 11) ^ bitrr(e, 25)
# 			# ch := (e and f) xor ((not e) and g)
# 			var ch = (e & f) ^ ((e ^ (0-1)) & g)
# 			# temp1 := h + S1 + ch + k[i] + w[i]
# 			var temp1 = h + S1 + ch + k[i] + w[i]
# 			# S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)
# 			var S0 = bitrr(a, 2) ^ bitrr(a, 13) ^ bitrr(a, 22)
# 			# maj := (a and b) xor (a and c) xor (b and c)
# 			var maj = (a & b) ^ (a & c) ^ (b & c)
# 			# temp2 := S0 + maj
# 			var temp2 = S0 + maj

# 			h = g
# 			g = f
# 			f = e
# 			e = d + temp1
# 			d = c
# 			c = b
# 			b = a
# 			a = temp1 + temp2

# 		h0 = h0 + a
# 		h1 = h1 + b
# 		h2 = h2 + c
# 		h3 = h3 + d
# 		h4 = h4 + e
# 		h5 = h5 + f
# 		h6 = h6 + g
# 		h7 = h7 + h



# 	var output = InlinedFixedVector[UInt8, 32](32)

# 	var digest_part_h0 = big_endian_dword_to_bytes(h0)
# 	for i in range(4):
# 		output.append(digest_part_h0[i])
# 	var digest_part_h1 = big_endian_dword_to_bytes(h1)
# 	for i in range(4):
# 		output.append(digest_part_h1[i])
# 	var digest_part_h2 = big_endian_dword_to_bytes(h2)
# 	for i in range(4):
# 		output.append(digest_part_h2[i])
# 	var digest_part_h3 = big_endian_dword_to_bytes(h3)
# 	for i in range(4):
# 		output.append(digest_part_h3[i])
# 	var digest_part_h4 = big_endian_dword_to_bytes(h4)
# 	for i in range(4):
# 		output.append(digest_part_h4[i])
# 	var digest_part_h5 = big_endian_dword_to_bytes(h5)
# 	for i in range(4):
# 		output.append(digest_part_h5[i])
# 	var digest_part_h6 = big_endian_dword_to_bytes(h6)
# 	for i in range(4):
# 		output.append(digest_part_h6[i])
# 	var digest_part_h7 = big_endian_dword_to_bytes(h7)
# 	for i in range(4):
# 		output.append(digest_part_h7[i])

# 	return output


--- wyhasher/__init__.mojo ---

from .wyhasher import wyhash

--- wyhasher/wyhasher.mojo ---


from bit import bit_width, byte_swap
from bit import rotate_bits_right

alias U128 = SIMD[DType.uint64, 2]
alias U256 = SIMD[DType.uint64, 4]
alias default_secret = SIMD[DType.uint64, 4](0x2d358dccaa6c78a5, 0x8bb84b93962eacc9, 0x4b33a62ed433d4a3, 0x4d5a2da51de1aa47)

@always_inline
fn wymum_32(inout a: UInt64, inout b: UInt64):
    var ab = U128(a, b)
    var abl = ab & 0xff_ff_ff_ff
    var abh = ab >> 32
    var hh = abh.reduce_mul()
    var hl = abh[0] * abl[1]
    var ll = abl.reduce_mul()
    var lh = abl[0] * abh[1]
    a, b = rotate_bits_right[32](hl) ^ hh, rotate_bits_right[32](lh) ^ ll

@always_inline
fn wymum(inout a: UInt64, inout b: UInt64):
    var ab = U128(a, b)
    var abl = ab & 0xff_ff_ff_ff
    var abh = ab >> 32
    var hh = abh.reduce_mul()
    var hl = abh[0] * abl[1]
    var ll = abl.reduce_mul()
    var lh = abl[0] * abh[1]
    var t = ll + (hl << 32)
    var lo = t + (lh << 32)
    var c = (t < ll).cast[DType.uint64]()
    c += (lo < t).cast[DType.uint64]()
    var hi = hh + (hl >> 32) + (lh >> 32) + c
    a, b = lo, hi

@always_inline
fn wy_mix(_a: UInt64, _b: UInt64) -> UInt64:
    var a = _a
    var b = _b
    wymum(a, b)
    return a ^ b

@always_inline
fn wyr8(p: UnsafePointer[UInt8]) -> UInt64:
    return p.bitcast[DType.uint64]().load()

@always_inline
fn wyr4(p: UnsafePointer[UInt8]) -> UInt64:
    return p.bitcast[DType.uint32]().load().cast[DType.uint64]()

@always_inline
fn wyr3(p: UnsafePointer[UInt8], k: Int) -> UInt64:
    return (p.load().cast[DType.uint64]() << 16) 
        | (p.offset(k >> 1).load().cast[DType.uint64]() << 8)
        | p.offset(k - 1).load().cast[DType.uint64]()

fn wyhash(key: String, _seed: UInt64, secret: U256 = default_secret) -> UInt64:
    var length = len(key)
    var p = UnsafePointer(key.unsafe_ptr())
    var seed = _seed ^ wy_mix(_seed ^ secret[0], secret[1])
    var a: UInt64 = 0
    var b: UInt64 = 0
    if length <= 16:
        if length >= 4:
            var last_part_index = (length >> 3) << 2
            a = (wyr4(p) << 32) | wyr4(p.offset(last_part_index))
            b = (wyr4(p.offset(length - 4)) << 32) | wyr4(p.offset(length - 4 - last_part_index))
        elif length > 0:
            a = wyr3(p, length)
    else:
        var see1 = seed
        var see2 = seed

        while length >= 48:
            var p64 = p.bitcast[DType.uint64]()
            var data1 = p64.load[width=4]()
            var data2 = p64.load[width=2]()
            var seed_values1 = U256(secret[1], seed, secret[2], see1)
            var seed_values2 = U128(secret[3], see2)
            var seeded_data1 = data1 ^ seed_values1
            var seeded_data2 = data2 ^ seed_values2
            seed = wy_mix(seed_values1[0], seed_values1[1])
            see1 = wy_mix(seed_values1[2], seed_values1[3])
            see2 = wy_mix(seed_values2[0], seed_values2[1])
            p = p.offset(48)
            length -= 48
        
        seed ^= see1 ^ see2

        while length > 16:
            var p64 = p.bitcast[DType.uint64]()
            var data = p64.load[width=2]()
            var seed_values = U128(secret[1], seed)
            var seeded_data = data ^ seed_values
            seed = wy_mix(seeded_data[0], seeded_data[1])
            p = p.offset(16)
            length -= 16
        a = wyr8(p.offset(length-16))
        b = wyr8(p.offset(length-8))

    a ^= secret[1]
    b ^= seed
    wymum(a, b)

    return wy_mix(a ^ secret[0] ^ len(key), b ^ secret[1])



--- C/README.md ---

# Compiling
```gcc -Wall -Wextra -O3 -o kittenJPEG kittenJPEG.c -lm```

# Usage
```./main filename.jpg```

It will create a decoded ppm file named decodedimage.ppm


--- C/main.c ---

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include <err.h>
#include <time.h>


const char * comp_names[3]={"Y","Cb","Cr"};

typedef struct
{
	uint_fast8_t H;
	uint_fast8_t V;
	uint_fast8_t Tq;
	uint_fast16_t xi;
	uint_fast16_t yi;
	uint_fast8_t Td; //quant table for DC
	uint_fast8_t Ta; //quant table for AC
} components_data_t;

typedef struct
{
	uint_fast8_t sz;
	uint_fast16_t codeword;
	uint_fast8_t decoded;
} huffman_entry_t;

typedef struct
{
	uint_fast8_t nb_entries;
	huffman_entry_t entries[256];
} huffman_table_t;

typedef uint_fast8_t quantization_table_t[8][8];

typedef struct
{
	double Y;
	double Cb;
	double Cr;
} pixel_YCbCr_t;

typedef struct
{
	uint8_t * data;
	
	uint_fast32_t filesize;
	
	uint_fast32_t pos_in_file;	
	
	uint_fast16_t size_X;
	uint_fast16_t size_Y;
	
	uint_fast8_t Hmax;
	uint_fast8_t Vmax;
	
	uint_fast32_t nb_MCU_total;
	
	uint8_t * compressed_pixeldata;
	
	uint_fast32_t sz_compressed_pixeldata;
	
	uint_fast32_t pos_compressed_pixeldata;
	
	uint_fast32_t bitpos_in_compressed_pixeldata;

	uint_fast8_t nb_components;
	
	components_data_t components_data[4];
	
	huffman_table_t huff_tables[2][2]; //Tc (type=AC/DC), Th (destination identifier)
	
	quantization_table_t quant_tables[4];
	
	pixel_YCbCr_t ** pixels_YCbCr;
	
} picture_t;

typedef double matrix8x8_t[8][8];

uint8_t get1i(uint8_t const * const data, uint_fast32_t * const pos)
{
	uint8_t val=data[*pos];
	(*pos)++;
	return val;
}

uint16_t get2i(uint8_t const * const data, uint_fast32_t * const pos)
{
	uint16_t val=(data[*pos]<<8)|data[(*pos)+1];
	(*pos)+=2;
	return val;
}

uint32_t get4i(uint8_t const * const data, uint_fast32_t * const pos)
{
	uint32_t val=(data[*pos]<<24)|(data[(*pos)+1]<<16)|(data[(*pos)+2]<<8)|(data[(*pos)+3]);
	(*pos)+=4;
	return val;
}

uint16_t get_marker(uint8_t const * const data, uint_fast32_t * const pos)
{
	return get2i(data, pos);
}


char *to_bin(const uint16_t word, const uint8_t sz)
{
	static char str[17];
	memset(str, '\0', 17);
	
	uint8_t i;
	for(i=0; i<sz; i++)
		str[i]='0'+!!(word&(1<<(sz-i-1)));
	
	return str;
}


uint_fast32_t ceil_to_multiple_of(const uint_fast32_t val, const uint_fast32_t multiple)
{
	return (uint_fast32_t)(multiple*ceil((double)val/multiple));
}

void skip_EXIF(picture_t * const pic)
{
	uint16_t len=get2i(pic->data, &(pic->pos_in_file));
	printf("APP1 (probably EXIF) found (length %u bytes), skipping\n", len);
	pic->pos_in_file+=len-2;
}

void parse_APP0(picture_t * const pic)
{
    uint16_t len = get2i(pic->data, &(pic->pos_in_file));
    printf("APP0 found (length %u bytes)\n", len);
    if (len < 16)
        errx(1, "APP0: too short");
    
    uint8_t identifier[5];
    memcpy(identifier, &pic->data[pic->pos_in_file], 5);
    pic->pos_in_file += 5;

    uint_fast8_t version_major = get1i(pic->data, &(pic->pos_in_file));
    uint_fast8_t version_minor = get1i(pic->data, &(pic->pos_in_file));
    uint_fast8_t units = get1i(pic->data, &(pic->pos_in_file));
    uint_fast16_t Xdensity = get2i(pic->data, &(pic->pos_in_file));
    uint_fast16_t Ydensity = get2i(pic->data, &(pic->pos_in_file));
    uint_fast16_t Xthumbnail = get1i(pic->data, &(pic->pos_in_file));
    uint_fast16_t Ythumbnail = get1i(pic->data, &(pic->pos_in_file));
        
    if (memcmp(identifier, "JFIF\x00", 5))
        errx(1, "APP0: invalid identifier");
    
    printf("version %u.%u\n", version_major, version_minor);
    printf("units %u\n", units);
    printf("density X %lu Y %lu\n", Xdensity, Ydensity);
        
    uint_fast32_t bytes_thumbnail = 3 * Xthumbnail * Ythumbnail;
    
    if (bytes_thumbnail)
    {
        printf("thumbnail %lu bytes, skipping\n", bytes_thumbnail);
        pic->pos_in_file += bytes_thumbnail;
    }
    else
        printf("no thumbnail\n");
	printf("parse_APP0 at end get2i pic->pos_in_file: %lu\n", (unsigned long)pic->pos_in_file);
}

void parse_DQT(picture_t * const pic)
{
	uint16_t Lq=get2i(pic->data, &(pic->pos_in_file));
	printf("DQT found (length %u bytes)\n", Lq);
	
	uint8_t PqTq=get1i(pic->data, &(pic->pos_in_file));
	uint8_t Pq=(PqTq>>4)&0x0f;
	uint8_t Tq=PqTq&0x0f;
	printf("Pq (element precision) %u -> %u bits\n", Pq, (Pq==0)?8:16);
	printf("Tq (table destination identifier) %u\n", Tq);
	
	if(Pq!=0)
		errx(1, "DQT: only 8 bit precision supported");
	
	uint16_t nb_data_bytes=Lq-2-1;
	
	if(nb_data_bytes!=64)
		errx(1, "DQT: nb_data_bytes!=64");

	uint8_t u,v;
	for(u=0; u<8; u++)
	{
		for(v=0; v<8; v++)
		{
			uint8_t Q=get1i(pic->data, &(pic->pos_in_file));
			pic->quant_tables[Tq][u][v]=Q;
		}
	}
	printf("\n");
}

void parse_SOF0(picture_t * const pic)
{
	uint16_t len=get2i(pic->data, &(pic->pos_in_file));
	printf("SOF0 found (length %u bytes)\n", len);
	
	uint_fast8_t P=get1i(pic->data, &(pic->pos_in_file));
	uint_fast16_t Y=get2i(pic->data, &(pic->pos_in_file));
	uint_fast16_t X=get2i(pic->data, &(pic->pos_in_file));
	uint_fast8_t Nf=get1i(pic->data, &(pic->pos_in_file));
	
	if(P!=8)
		errx(1, "SOF0: P!=8 unsupported");
	
	if(Y==0)
		errx(1, "SOF0: Y==0 unsupported");
	
	printf("P %u (must be 8)\n", P);
	printf("imagesize X %lu Y %lu\n", X, Y);
	printf("Nf (number of components) %u\n", Nf);
	
	if(Nf!=3)
		errx(1, "picture does not have 3 components, this code will not work");
	
	pic->size_X=X;
	pic->size_Y=Y;
	
	uint_fast8_t i;
	for(i=0; i<Nf; i++)
	{
		uint8_t C=get1i(pic->data, &(pic->pos_in_file));
		uint8_t HV=get1i(pic->data, &(pic->pos_in_file));
		uint8_t H=(HV>>4)&0x0f;
		uint8_t V=HV&0x0f;
		uint8_t Tq=get1i(pic->data, &(pic->pos_in_file));
		
		pic->components_data[i].H=H;
		pic->components_data[i].V=V;
		pic->components_data[i].Tq=Tq;
		
		printf("component %u (%s) C %u, H %u, V %u, Tq %u\n", i, comp_names[i], C, H, V, Tq);
	}
	
	pic->nb_components=Nf;
	
	uint_fast8_t Hmax=0,Vmax=0;

	for(i=0; i<pic->nb_components; i++)
	{
		if(pic->components_data[i].H>Hmax)
			Hmax=pic->components_data[i].H;
		if(pic->components_data[i].V>Vmax)
			Vmax=pic->components_data[i].V;
	}
	
	pic->Hmax=Hmax;
	pic->Vmax=Vmax;
	
	pic->nb_MCU_total=(ceil_to_multiple_of(pic->size_X, 8*Hmax)/(8*Hmax))*(ceil_to_multiple_of(pic->size_Y, 8*Hmax)/(8*Vmax));
	
	printf("Hmax %u Vmax %u\n", Hmax, Vmax);
	printf("MCU_total %lu\n", pic->nb_MCU_total);
	
	uint16_t xi,yi;
	for(i=0; i<pic->nb_components; i++)
	{
		xi=(uint16_t)ceil((double)pic->size_X*pic->components_data[i].H/Hmax);
		yi=(uint16_t)ceil((double)pic->size_Y*pic->components_data[i].V/Vmax);
		
		pic->components_data[i].xi=xi;
		pic->components_data[i].yi=yi;
		
		printf("component %u (%s) xi %u yi %u\n", i, comp_names[i], xi, yi);
	}
	
	printf("allocating memory for pixels\n");
	
	uint_fast16_t x,y;
	pic->pixels_YCbCr=malloc(pic->size_X*sizeof(pixel_YCbCr_t*));
	for(x=0; x<pic->size_X; x++)
		pic->pixels_YCbCr[x]=malloc(pic->size_Y*sizeof(pixel_YCbCr_t));
	
	for(x=0; x<pic->size_X; x++)
	{
		for(y=0; y<pic->size_Y; y++)
		{
			pic->pixels_YCbCr[x][y].Y=0;
			pic->pixels_YCbCr[x][y].Cb=0;
			pic->pixels_YCbCr[x][y].Cr=0;
		}
	}
	printf("memory allocated\n");
}

void parse_DHT(picture_t * const pic)
{
	uint16_t len=get2i(pic->data, &(pic->pos_in_file));
	printf("DHT found (length %u bytes)\n", len);
	
	uint8_t TcTh=get1i(pic->data, &(pic->pos_in_file));
	uint8_t Tc=(TcTh>>4)&0x0f;
	uint8_t Th=TcTh&0x0f;
	
	printf("Tc %u (%s table)\n", Tc, (Tc==0)?"DC":"AC");
	printf("Th (table destination identifier) %u\n", Th);
	
	uint8_t L[16];
	uint8_t mt=0;
	uint8_t i;
	for(i=0; i<16; i++)
	{
		L[i]=get1i(pic->data, &(pic->pos_in_file));
		mt+=L[i];
	}
	
	printf("total %u codes\n", mt);

	uint16_t codeword=0;
	
	for(i=0; i<16; i++)
	{
		uint8_t j;
		for(j=0; j<L[i]; j++)
		{
			uint8_t V=get1i(pic->data, &(pic->pos_in_file));

			pic->huff_tables[Tc][Th].entries[pic->huff_tables[Tc][Th].nb_entries].sz=i+1;
			pic->huff_tables[Tc][Th].entries[pic->huff_tables[Tc][Th].nb_entries].codeword=codeword;
			pic->huff_tables[Tc][Th].entries[pic->huff_tables[Tc][Th].nb_entries].decoded=V;
			pic->huff_tables[Tc][Th].nb_entries++;
			
			codeword++;
		}
		codeword<<=1;
	}
}

void parse_SOS(picture_t * const pic)
{
	uint16_t len=get2i(pic->data, &(pic->pos_in_file)); //without actual bitmap data
	printf("SOS found (length %u bytes)\n", len);
	
	uint8_t Ns=get1i(pic->data, &(pic->pos_in_file));
	printf("Ns %u\n", Ns);
	
	uint8_t j;
	for(j=0; j<Ns; j++)
	{
		uint8_t Cs=get1i(pic->data, &(pic->pos_in_file));
		uint8_t TdTa=get1i(pic->data, &(pic->pos_in_file));
		uint8_t Td=(TdTa>>4)&0x0f;
		uint8_t Ta=TdTa&0x0f;
		
		printf("component %u (%s) Cs %u Td %u Ta %u\n", j, comp_names[j], Cs, Td, Ta);
		pic->components_data[j].Td=Td; //DC
		pic->components_data[j].Ta=Ta; //AC
	}
	
	uint8_t Ss=get1i(pic->data, &(pic->pos_in_file));
	uint8_t Se=get1i(pic->data, &(pic->pos_in_file));
	uint8_t AhAl=get1i(pic->data, &(pic->pos_in_file));
	uint8_t Ah=(AhAl>>4)&0x0f;
	uint8_t Al=AhAl&0x0f;
	
	printf("Ss %u Se %u Ah %u Al %u\n", Ss, Se, Ah, Al);
	
	pic->pos_compressed_pixeldata=pic->pos_in_file;
	
	printf("compressed pixeldata starts at pos %lu\n\n", pic->pos_compressed_pixeldata);
}

void copy_bitmap_data_remove_stuffing(picture_t * const pic)
{
	printf("removing stuffing...\n");
	
	//get length of bitstream without stuffing
	
	uint_fast32_t pos=pic->pos_compressed_pixeldata;
	uint_fast32_t size=0;
	uint8_t byte;
	uint16_t combined=0;
	
	do
	{
		if(pos>=pic->filesize)
			errx(1, "marker EOI (0xFFD9) missing");
		
		byte=pic->data[pos++];
		if(byte==0xFF)
		{
			uint8_t byte2=pic->data[pos++];
			if(byte2!=0x00)
				combined=(byte<<8)|byte2;
			else
				size++;
		}
		else
			size++;
	} while(combined!=0xFFD9);
	
	uint_fast32_t size_stuffed=pos-pic->pos_compressed_pixeldata-2;
	
	//remove stuffing
	
	pic->compressed_pixeldata=malloc(size*sizeof(uint8_t));
	if(!pic->compressed_pixeldata)
		err(1, "malloc");
	
	printf("%lu bytes with stuffing\n", size_stuffed);
	
	uint_fast32_t i;
	uint_fast32_t size_without_stuffing;
	
	for(i=pic->pos_compressed_pixeldata, pos=0, size_without_stuffing=0; i<(pic->pos_compressed_pixeldata+size_stuffed); )
	{
		if(pic->data[i]!=0xFF)
		{
			pic->compressed_pixeldata[pos++]=pic->data[i++];
			size_without_stuffing++;
		}
		else if(pic->data[i]==0xFF && pic->data[i+1]==0x00)
		{
			pic->compressed_pixeldata[pos++]=0xFF;
			size_without_stuffing++;
			i+=2;
		}
		else
			errx(1, "unexpected marker 0x%02x%02x found in bitstream", pic->data[i], pic->data[i+1]);
	}
	
	pic->bitpos_in_compressed_pixeldata=0;
	pic->sz_compressed_pixeldata=size_without_stuffing;
	pic->pos_in_file=pic->pos_compressed_pixeldata+size_stuffed;
	
	printf("%lu data bytes without stuffing\n\n", size_without_stuffing);
}

int16_t convert_to_neg(uint16_t bits, const uint8_t sz)
{
	int16_t ret=-((bits^0xFFFF)&((1<<sz)-1));
	return ret;
}


uint16_t bitstream_get_bits(picture_t * const pic, const uint_fast8_t nb_bits)
{
	if(nb_bits>16)
		errx(1, "bitstream_get_bits: >16 bits requested");
	
	uint_fast32_t index=pic->bitpos_in_compressed_pixeldata/8;
	int_fast8_t pos_in_byte=(7-pic->bitpos_in_compressed_pixeldata%8);
	uint16_t ret=0;
	uint_fast8_t bits_copied=0;
	
	while(pos_in_byte>=0 && bits_copied<nb_bits)
	{
		ret<<=1;
		ret|=!!(pic->compressed_pixeldata[index]&(1<<pos_in_byte));
		bits_copied++;
		pos_in_byte--;
		if(pos_in_byte<0)
		{
			pos_in_byte=7;
			index++;
		}
	}
	
	return ret;	
}

void bitstream_remove_bits(picture_t * const pic, const uint_fast8_t nb_bits)
{
	pic->bitpos_in_compressed_pixeldata+=nb_bits;
}


bool huff_decode(picture_t * const pic, const uint8_t Tc, const uint8_t Th, const uint8_t sz, const uint16_t bitstream, uint8_t * const decoded)
{
	uint32_t i;
	for(i=0; i<pic->huff_tables[Tc][Th].nb_entries; i++)
	{
		if(pic->huff_tables[Tc][Th].entries[i].sz==sz && pic->huff_tables[Tc][Th].entries[i].codeword==bitstream)
		{
			(*decoded)=pic->huff_tables[Tc][Th].entries[i].decoded;
			return true;
		}
	}
	
	return false;
}


bool bitstream_get_next_decoded_element(picture_t * const pic, const uint8_t Tc, const uint8_t Th, uint8_t * const decoded, uint_fast8_t * const nb_bits)
{
	uint16_t huff_candidate;
	bool found;
	
	while(pic->bitpos_in_compressed_pixeldata<8*pic->sz_compressed_pixeldata)
	{
		found=false;
		for(*nb_bits=1; *nb_bits<=16; (*nb_bits)++)
		{
			if((pic->bitpos_in_compressed_pixeldata+*nb_bits)>8*pic->sz_compressed_pixeldata)
				errx(1, "end of stream, requested to many bits");
			
			huff_candidate=bitstream_get_bits(pic, *nb_bits);
			if(huff_decode(pic, Tc, Th, *nb_bits, huff_candidate, decoded))
			{
				found=true;
				bitstream_remove_bits(pic, *nb_bits);

				return true;
			}
		}
		if(!found)
		{
			//check if it's padding, else error
			bool is_all_one=true;
			uint_fast8_t i;
			for(i=0; i<(*nb_bits)-1; i++)
			{
				if((huff_candidate&(1<<i))==0)
				{
					is_all_one=false;
					break;
				}
			}
			
			if(is_all_one) //padding
				bitstream_remove_bits(pic, *nb_bits);
			else
				errx(1, "unknown code in bitstream bitpos %lu byte 0x%x [prev 0x%x, next 0x%x]", pic->bitpos_in_compressed_pixeldata, pic->compressed_pixeldata[pic->bitpos_in_compressed_pixeldata/8], pic->compressed_pixeldata[(pic->bitpos_in_compressed_pixeldata/8)-1], pic->compressed_pixeldata[(pic->bitpos_in_compressed_pixeldata/8)+1]);
		}
	}

	return false;
}


void store_data_unit_YCbCr(picture_t * const pic, const uint_fast32_t MCU, const uint_fast8_t component, const uint_fast8_t data_unit, const matrix8x8_t data)
{
	uint_fast8_t zoomX, zoomY;
	
	zoomX=pic->Hmax/pic->components_data[component].H;
	zoomY=pic->Vmax/pic->components_data[component].V;
	
	uint_fast8_t scaleX, scaleY;
	
	scaleX=8*pic->components_data[component].H;
	scaleY=8*pic->components_data[component].V;
	
	uint_fast16_t startX, startY;
	
	startX=MCU%(ceil_to_multiple_of(pic->size_X, 8*pic->Hmax)/(scaleX*zoomX));
	startY=MCU/(ceil_to_multiple_of(pic->size_X, 8*pic->Hmax)/(scaleY*zoomY)); //yes, size_X and H!
	
	uint_fast16_t startHiX=data_unit%pic->components_data[component].H;
	uint_fast16_t startHiY=data_unit/pic->components_data[component].H; //yes, H!
	
	uint_fast8_t x,y;
	uint_fast8_t zx,zy;
	
	uint_fast32_t posX, posY;
	
	for(x=0; x<8; x++)
	{
		for(y=0; y<8; y++)
		{
			for(zx=0; zx<zoomX; zx++)
			{
				for(zy=0; zy<zoomY; zy++)
				{
					posX=(scaleX*startX+8*startHiX+x)*zoomX+zx;
					posY=(scaleY*startY+8*startHiY+y)*zoomY+zy;
					
					if(posX<pic->size_X && posY<pic->size_Y)
					{			
						switch(component)
						{
							case 0: pic->pixels_YCbCr[posX][posY].Y=data[x][y]; break;
							case 1: pic->pixels_YCbCr[posX][posY].Cb=data[x][y]; break;
							case 2: pic->pixels_YCbCr[posX][posY].Cr=data[x][y]; break;
							default: errx(1, "unknown component"); break;
						}
					}
				}
			}
		}
	}
}


void reverse_ZZ_and_dequant(picture_t const * const pic, const uint8_t quant_table, const matrix8x8_t inp, matrix8x8_t outp)
{
	const uint_fast8_t reverse_ZZ_u[8][8]={	{0, 0, 1, 2, 1, 0, 0, 1 },
											{2, 3, 4, 3, 2, 1, 0, 0 },
											{1, 2, 3, 4, 5, 6, 5, 4 },
											{3, 2, 1, 0, 0, 1, 2, 3 },
											{4, 5, 6, 7, 7, 6, 5, 4 },
											{3, 2, 1, 2, 3, 4, 5, 6 },
											{7, 7, 6, 5, 4, 3, 4, 5 },
											{6, 7, 7, 6, 5, 6, 7, 7 }	};
										
	const uint_fast8_t reverse_ZZ_v[8][8]={	{0, 1, 0, 0, 1, 2, 3, 2 },
											{1, 0, 0, 1, 2, 3, 4, 5 },
											{4, 3, 2, 1, 0, 0, 1, 2 },
											{3, 4, 5, 6, 7, 6, 5, 4 },
											{3, 2, 1, 0, 1, 2, 3, 4 },
											{5, 6, 7, 7, 6, 5, 4, 3 },
											{2, 3, 4, 5, 6, 7, 7, 6 },
											{5, 4, 5, 6, 7, 7, 6, 7 }	};
	
	uint_fast8_t u,v;
	
	for(u=0; u<8; u++)
		for(v=0; v<8; v++)
			outp[reverse_ZZ_u[u][v]][reverse_ZZ_v[u][v]]=inp[u][v]*pic->quant_tables[quant_table][u][v];
}

#define a_c 0.9807
#define b_c 0.8314
#define c_c 0.5555
#define d_c 0.1950
#define e_c 0.9238
#define f_c 0.3826
#define g_c 0.7071


static float tab_coefs[8][8] = {{0.7071,  a_c,  e_c,  b_c,  g_c,  c_c,  f_c,  d_c},
                                {0.7071,  b_c,  f_c, -d_c, -g_c, -a_c, -e_c, -c_c},
                                {0.7071,  c_c, -f_c, -a_c, -g_c,  d_c,  e_c,  b_c},
                                {0.7071,  d_c, -e_c, -c_c,  g_c,  b_c, -f_c, -a_c},
                                {0.7071, -d_c, -e_c,  c_c,  g_c, -b_c, -f_c,  a_c},
                                {0.7071, -c_c, -f_c,  a_c, -g_c, -d_c,  e_c, -b_c},
                                {0.7071, -b_c,  f_c,  d_c, -g_c,  a_c, -e_c,  c_c},
                                {0.7071, -a_c,  e_c, -b_c,  g_c, -c_c,  f_c, -d_c}};


void data_unit_do_idct(const matrix8x8_t inp, matrix8x8_t outp)
{
    double rxy=0;
  
    uint_fast8_t x, y;
    uint_fast8_t u, v;
        
    for(y=0; y<8; y++)
    {
        for(x=0; x<8; x++)
        {
        rxy=0;

        for(u=0; u<=7; u++)
        {
            for(v=0; v<=7; v++)
            {
            double Svu=inp[v][u];

            rxy+= Svu*tab_coefs[x][u]*tab_coefs[y][v];
            }
        }
                
        rxy*=0.25;
        rxy+=128;

        outp[x][y]=rxy;
        }
    }
}

void print_matrix(matrix8x8_t m)
{
	for(int u = 0; u < 8; u++) {
		for(int v = 0; v < 8; v++) {
			printf("%5f ", m[u][v]);
		}
		printf("\n");
	}
	printf("\n");
}

void parse_bitmap_data(picture_t * const pic)
{
	printf("parsing bitstream...\n");
	
	uint_fast8_t nb_bits;
	uint_fast8_t component=0; //Cs
	uint_fast8_t data_unit;
	uint_fast8_t u,v;
	uint_fast8_t ac_count;
	
	int16_t precedent_DC[4]={0,0,0,0};
	
	uint_fast32_t nb_MCU=0;

	matrix8x8_t matrix;
	
	for(nb_MCU=0; nb_MCU<pic->nb_MCU_total; nb_MCU++)
	{
		for(component=0; component<pic->nb_components; component++)
		{
			for(data_unit=0; data_unit<(pic->components_data[component].V*pic->components_data[component].H); data_unit++)
			{

				for(u=0; u<8; u++)
					for(v=0; v<8; v++)
						matrix[u][v]=0;
				
				uint8_t SSSS;
				int16_t DC;
				if(!bitstream_get_next_decoded_element(pic, 0, pic->components_data[component].Td, &SSSS, &nb_bits))
					errx(1, "no DC data");
				if(SSSS)
				{
					uint16_t bits_DC=bitstream_get_bits(pic, SSSS);
					bitstream_remove_bits(pic, SSSS);
					
					bool msb_DC=!!(bits_DC&(1<<(SSSS-1)));
					
					if(msb_DC)
						DC=precedent_DC[component]+bits_DC;
					else
						DC=precedent_DC[component]+convert_to_neg(bits_DC,SSSS);
					
				}
				else
					DC=precedent_DC[component]+0;
				
				matrix[0][0]=DC;
				precedent_DC[component]=DC;

				int16_t AC;
				for(ac_count=0; ac_count<63; )
				{
					uint8_t RRRRSSSS;
					if(!bitstream_get_next_decoded_element(pic, 1, pic->components_data[component].Ta, &RRRRSSSS, &nb_bits))
						errx(1, "no AC data");
					
					uint8_t RRRR=(RRRRSSSS>>4); //number of preceding 0 samples
					uint8_t SSSS=RRRRSSSS&0x0f; //category
					
					if(RRRR==0 && SSSS==0)
					{

						break;
					}
					else if(RRRR==0x0F && SSSS==0)
					{
						ac_count+=16;

					}
					else
					{
						ac_count+=RRRR;
						
						uint16_t bits_AC=bitstream_get_bits(pic, SSSS);
						bitstream_remove_bits(pic, SSSS);
						
						bool msb_AC=!!(bits_AC&(1<<(SSSS-1)));
						
						if(msb_AC)
							AC=bits_AC;
						else
							AC=convert_to_neg(bits_AC,SSSS);
						
						u=(ac_count+1)/8;
						v=(ac_count+1)%8;
						matrix[u][v]=AC;
						ac_count++;

					}
				}

				matrix8x8_t matrix_dequant;

				reverse_ZZ_and_dequant(pic, pic->components_data[component].Tq, matrix, matrix_dequant);
				matrix8x8_t matrix_decoded;

				data_unit_do_idct(matrix_dequant, matrix_decoded);
				store_data_unit_YCbCr(pic, nb_MCU, component, data_unit, matrix_decoded);
			}
		}
	}
	printf("parsed %lu MCU\n", nb_MCU);
}


void open_new_picture(char const * const name, picture_t * const picture)
{
	FILE *f=fopen(name, "rb");
	if(!f)
		err(1, "fopen %s failed", name);
	
	fseek(f, 0, SEEK_END);
	picture->filesize=ftell(f);
	fseek(f, 0, SEEK_SET);
	
	picture->data=malloc(picture->filesize*sizeof(uint8_t));
	if(!picture->data)
		err(1, "malloc for %s failed", name);
		
	if(fread(picture->data, picture->filesize, 1, f)!=1)
		err(1, "fread for %s failed", name);
		
	fclose(f);
	
	printf("%lu bytes read from %s\n\n", picture->filesize, name);
	
	picture->pos_in_file=0;
	
	picture->nb_components=0;
	
	picture->huff_tables[0][0].nb_entries=0;
	picture->huff_tables[0][1].nb_entries=0;
	picture->huff_tables[1][0].nb_entries=0;
	picture->huff_tables[1][1].nb_entries=0;
}

void parse_picture(picture_t * const picture)
{
	while(picture->pos_in_file<=picture->filesize-2)
	{
		uint16_t marker;
		
		marker=get_marker(picture->data, &(picture->pos_in_file));
		printf("marker: %d\n",marker);
		
		switch(marker)
		{
			case 0xFFD8:	printf("SOI found\n"); break;
			
			case 0xFFE1:	skip_EXIF(picture); break;
			
			case 0xFFE0:	parse_APP0(picture); break;
			case 0xFFDB:	parse_DQT(picture); break;
			case 0xFFC0:	parse_SOF0(picture); break;
			case 0xFFC4:	parse_DHT(picture); break;
			case 0xFFDA:	parse_SOS(picture);
							copy_bitmap_data_remove_stuffing(picture);
							parse_bitmap_data(picture);
							break;
			
			case 0xFFD9:	printf("EOI found\n"); break;
			
			default: errx(1, "unknown marker 0x%04x pos %lu", marker, picture->pos_in_file); break;
		}
		printf("\n");
	}
}

uint8_t clamp(const double v)
{
	if(v<0)
		return 0;
	if(v>255)
		return 255;
	
	return (uint8_t)v;
}

void write_ppm(picture_t const * const pic, char const * const filename)
{
	uint_fast16_t x,y;
	
	double Y,Cb,Cr;
	double r,g,b;
	
	FILE *out=fopen(filename, "w");
	
	printf("writing file %s\n", filename);
	
	fprintf(out, "P3\n%lu %lu\n255\n", pic->size_X, pic->size_Y);
		
	for(y=0; y<pic->size_Y; y++)
	{
		for(x=0; x<pic->size_X; x++)
		{
			Y=pic->pixels_YCbCr[x][y].Y;
			Cb=pic->pixels_YCbCr[x][y].Cb;
			Cr=pic->pixels_YCbCr[x][y].Cr;
			
			r=Y+1.402*(Cr-128);
			g=Y-(0.114*1.772*(Cb-128)+0.299*1.402*(Cr-128))/0.587;
			b=Y+1.772*(Cb-128);
			
			fprintf(out, "%u %u %u ", clamp(round(r)),clamp(round(g)),clamp(round(b)));
		}
		fprintf(out, "\n");
	}
	fclose(out);
	printf("output file written\n\n");
}


int main(int argc, char *argv[])
{

	if (argc != 2) {
        printf("Usage: %s <filename.jpg>\n", argv[0]);
        return 1;
    }

	clock_t start_time, end_time, write_time;
    double cpu_time_used_algo, cpu_time_used_write;
	start_time = clock();
	picture_t pic;
	open_new_picture(argv[1], &pic);
	parse_picture(&pic);
	end_time = clock();
	write_ppm(&pic, "decodedimage.ppm");
	write_time = clock();
    cpu_time_used_algo = ((double) (end_time - start_time)) / CLOCKS_PER_SEC;
	cpu_time_used_write = ((double) (write_time - end_time)) / CLOCKS_PER_SEC;
    printf("Time taken by the Jpeg decoder algorithm: %f seconds\n", cpu_time_used_algo);
	printf("Time taken for writing the image: %f seconds\n", cpu_time_used_write);
}

--- Mojo/README.md ---

# Compiling

``` mojo build main.mojo ```

# Usage

Give the path of the image you want to decode at the bottom of the file. i.e, at line 1037:

```open_new_picture("../Example_images/test1.jpg", pic)```

Run the program using:

```./main```

It will output a decoded ppm file named decodedimage.ppm


--- Mojo/main.mojo ---

import os
from python import Python
from python import PythonObject
from math import sqrt
from time import now
import pathlib as path
import sys

struct Array1D(CollectionElement):
    var data: Pointer[UInt8]
    var size: Int

    fn __init__(inout self, size: Int, val: UInt8):
        self.size = size
        self.data = Pointer[UInt8].alloc(self.size)
        for i in range(self.size):
            self.data.store(i, val)

    fn __getitem__(self, i: Int) -> UInt8:
        return self.data.load(i)

    fn __setitem__(inout self, i: Int, val: UInt8):
        self.data.store(i, val)

    fn __copyinit__(inout self, existing: Self):
        self.size = existing.size
        self.data = Pointer[UInt8].alloc(self.size)
        for i in range(self.size):
            self.data.store(i, existing.data.load(i))

    fn __moveinit__(inout self, owned existing: Self):
        self.size = existing.size
        self.data = existing.data
        existing.data = Pointer[UInt8].alloc(0)  # Hollow out the existing value

    fn __del__(owned self):
        self.data.free()

struct Array1Dnew(CollectionElement):
    var data: Pointer[Int16]
    var size: Int

    fn __init__(inout self, size: Int, val: Int16):
        self.size = size
        self.data = Pointer[Int16].alloc(self.size)
        for i in range(self.size):
            self.data.store(i, val)

    fn __getitem__(self, i: Int) -> Int16:
        return self.data.load(i)

    fn __setitem__(inout self, i: Int, val: Int16):
        self.data.store(i, val)

    fn __copyinit__(inout self, existing: Self):
        self.size = existing.size
        self.data = Pointer[Int16].alloc(self.size)
        for i in range(self.size):
            self.data.store(i, existing.data.load(i))

    fn __moveinit__(inout self, owned existing: Self):
        self.size = existing.size
        self.data = existing.data
        existing.data = Pointer[Int16].alloc(0)

    fn __del__(owned self):
        self.data.free()

struct Array2D(CollectionElement):
    var data: Pointer[Float32]
    var dim0: Int
    var dim1: Int

    fn __init__(inout self, dim0: Int, dim1: Int):
        self.dim0 = dim0
        self.dim1 = dim1
        self.data = Pointer[Float32].alloc(dim0 * dim1)
    
    fn __copyinit__(inout self, other: Array2D):
        self.dim0 = other.dim0
        self.dim1 = other.dim1
        self.data = Pointer[Float32].alloc(self.dim0 * self.dim1)
        for i in range(self.dim0 * self.dim1):
            self.data.store(i, other.data.load(i))
    
    fn __moveinit__(inout self, owned existing: Array2D):
        self.dim0 = existing.dim0
        self.dim1 = existing.dim1
        self.data = existing.data

    fn __getitem__(borrowed self, i: Int, j: Int) -> Float32:
        return self.data.load(i * self.dim1 + j)

    fn __setitem__(inout self, i: Int, j: Int, value: Float32):
        self.data.store(i * self.dim1 + j, value)

    fn __del__(owned self):
        self.data.free()
    
    fn print_array(borrowed self):
        for i in range(self.dim0):
            for j in range(1):
                print(self.__getitem__(i, j), ' ', self.__getitem__(i, j+1), ' ', self.__getitem__(i, j+2), ' ', self.__getitem__(i, j+3), ' ',
                      self.__getitem__(i, j+4), ' ', self.__getitem__(i, j+5), ' ', self.__getitem__(i, j+6), ' ', self.__getitem__(i, j+7))

struct Array2Dnew(CollectionElement):
    var data: Pointer[SIMD[DType.float32, 8]]
    var dim: Int

    fn __init__(inout self, dim: Int):
        self.dim = dim
        self.data = Pointer[SIMD[DType.float32, 8]].alloc(dim)
    
    fn __copyinit__(inout self, other: Array2Dnew):
        self.dim = other.dim
        self.data = Pointer[SIMD[DType.float32, 8]].alloc(self.dim)
        for i in range(self.dim):
            self.data.store(i, other.data.load(i))
    
    fn __moveinit__(inout self, owned existing: Array2Dnew):
        self.dim = existing.dim
        self.data = existing.data

    fn __getitem__(borrowed self, i: Int) -> SIMD[DType.float32, 8]:
        return self.data.load(i)

    fn __setitem__(inout self, i: Int, value: SIMD[DType.float32, 8]):
        self.data.store(i, value)
    
    fn __setitem2__(inout self, i: Int, j: Int, value: Float32):
        var simd_vector = self.data.load(i)
        simd_vector[j] = value
        self.data.store(i, simd_vector)

    fn __del__(owned self):
        self.data.free()
    
    fn print_array(borrowed self):
        for i in range(self.dim):
            print(self.__getitem__(i))

struct Array3D(CollectionElement):
    var data: Pointer[UInt8]
    var dim0: Int
    var dim1: Int
    var dim2: Int

    fn __init__(inout self, dim0: Int, dim1: Int, dim2: Int):
        self.dim0 = dim0
        self.dim1 = dim1
        self.dim2 = dim2
        self.data = Pointer[UInt8].alloc(dim0 * dim1 * dim2)
    
    fn __copyinit__(inout self, other: Array3D):
        self.dim0 = other.dim0
        self.dim1 = other.dim1
        self.dim2 = other.dim2
        self.data = Pointer[UInt8].alloc(self.dim0 * self.dim1 * self.dim2)
        for i in range(self.dim0 * self.dim1 * self.dim2):
            self.data.store(i, other.data.load(i))
    
    fn __moveinit__(inout self, owned existing: Array3D):
        self.dim0 = existing.dim0
        self.dim1 = existing.dim1
        self.dim2 = existing.dim2
        self.data = existing.data

    fn __getitem__(borrowed self, i: Int, j: Int, k: Int) -> UInt8:
        return self.data.load(i * self.dim1 * self.dim2 + j * self.dim2 + k)

    fn __setitem__(inout self, i: SIMD[DType.uint8, 1], j: Int, k: Int, value: SIMD[DType.uint8, 1]):
        self.data.store(i * self.dim1 * self.dim2 + j * self.dim2 + k, value)

    fn __del__(owned self):
        self.data.free()


struct ComponentsData(CollectionElement):
    var H: SIMD[DType.uint32, 1]
    var V: SIMD[DType.uint32, 1]
    var Tq: SIMD[DType.uint32, 1]
    var xi: SIMD[DType.uint32, 1]
    var yi: SIMD[DType.uint32, 1]
    var Td: Int #quant table for DC
    var Ta: Int #quant table for AC

    fn __init__(inout self, H: Int, V: Int, Tq: Int, xi: Int, yi: Int, Td: Int, Ta: Int):
        self.H = H
        self.V = V
        self.Tq = Tq
        self.xi = xi
        self.yi = yi
        self.Td = Td
        self.Ta = Ta

    fn __copyinit__(inout self, existing: Self):
        self.H = existing.H
        self.V = existing.V
        self.Tq = existing.Tq
        self.xi = existing.xi
        self.yi = existing.yi
        self.Td = existing.Td
        self.Ta = existing.Ta

    fn __moveinit__(inout self, owned existing: Self):
        self.H = existing.H
        self.V = existing.V
        self.Tq = existing.Tq
        self.xi = existing.xi
        self.yi = existing.yi
        self.Td = existing.Td
        self.Ta = existing.Ta

@register_passable
struct HuffmanEntry(CollectionElement):
    var sz: Int
    var codeword: Int
    var decoded: Int

    fn __init__(sz: Int, codeword: Int, decoded: Int) -> Self:
        return Self{sz: sz, codeword: codeword, decoded: decoded}
    
    fn __copyinit__(existing) -> Self:
        return Self{sz: existing.sz, codeword:existing.codeword, decoded: existing.decoded}

@register_passable
struct HuffmanEntryArray(CollectionElement):
    var data: Pointer[HuffmanEntry]
    var size: Int

    fn __init__(size: Int) -> Self:
        return Self{size : size, data:Pointer[HuffmanEntry].alloc(size)}

    fn __copyinit__(existing: Self) -> Self:
        let newData = Pointer[HuffmanEntry].alloc(existing.size)
        for i in range(existing.size):
            newData.store(i, existing.data.load(i))
        return Self{size: existing.size, data: newData}

@register_passable
struct HuffmanTable(CollectionElement):
    var nb_entries: Int
    var entries: HuffmanEntryArray

    fn __init__(nb_entries: Int, entries: HuffmanEntryArray) -> Self:
        return Self{nb_entries: nb_entries, entries: entries}
    
    fn __copyinit__(existing) -> Self:
        return Self{nb_entries: existing.nb_entries, entries: existing.entries}

@register_passable
struct HuffmanTableArray:
    var data: Pointer[HuffmanTable]
    var size: Int
    fn __init__(size: Int) -> Self:
        return Self{size : size, data:Pointer[HuffmanTable].alloc(size)}

    fn __copyinit__(existing: Self) -> Self:
        let newData = Pointer[HuffmanTable].alloc(existing.size)
        for i in range(existing.size):
            newData.store(i, existing.data.load(i))
        return Self{size: existing.size, data: newData}


struct PixelYCbCr(CollectionElement):
    var Y: Float32
    var Cb: Float32
    var Cr: Float32

    fn __init__(inout self, Y: Float32, Cb: Float32, Cr: Float32):
        self.Y = Y
        self.Cb = Cb
        self.Cr = Cr

    fn __copyinit__(inout self, existing: Self):
        self.Y = existing.Y
        self.Cb = existing.Cb
        self.Cr = existing.Cr

    fn __moveinit__(inout self, owned existing: Self):
        self.Y = existing.Y
        self.Cb = existing.Cb
        self.Cr = existing.Cr

struct picture_t:
    var data: DynamicVector[UInt8]
    var helpdata: DynamicVector[UInt16]
    var stringdata: String
    var filesize: Int
    var pos_in_file: Int
    var size_X: SIMD[DType.uint32, 1]
    var size_Y: SIMD[DType.uint32, 1]
    var Hmax: SIMD[DType.uint32, 1]
    var Vmax: SIMD[DType.uint32, 1]
    var nb_MCU_total: SIMD[DType.uint32, 1]
    var compressed_pixeldata: DynamicVector[Int]
    var sz_compressed_pixeldata: Int
    var pos_compressed_pixeldata: Int
    var bitpos_in_compressed_pixeldata: Int
    var nb_components: SIMD[DType.uint32, 1]
    var components_data: DynamicVector[ComponentsData]
    var huff_tables1: HuffmanTableArray
    var huff_tables2: HuffmanTableArray
    var quant_table: Array3D
    var pixel_Y: Array2D
    var pixel_Cb: Array2D
    var pixel_Cr: Array2D

    fn __init__(inout self, data: DynamicVector[UInt8], helpdata: DynamicVector[UInt16], stringdata: String, filesize: Int, pos_in_file: Int, size_X: Int, size_Y: Int, Hmax: Int, Vmax: Int, nb_MCU_total: Int, compressed_pixeldata: DynamicVector[Int], sz_compressed_pixeldata: Int, pos_compressed_pixeldata: Int, bitpos_in_compressed_pixeldata: Int, nb_components: Int, components_data: DynamicVector[ComponentsData], huff_tables1: HuffmanTableArray, huff_tables2: HuffmanTableArray, quant_table: Array3D, pixel_Y: Array2D, pixel_Cb: Array2D, pixel_Cr: Array2D):
        self.data = data
        self.helpdata = helpdata
        self.stringdata = stringdata
        self.filesize = filesize
        self.pos_in_file = pos_in_file
        self.size_X = size_X
        self.size_Y = size_Y
        self.Hmax = Hmax
        self.Vmax = Vmax
        self.nb_MCU_total = nb_MCU_total
        self.compressed_pixeldata = compressed_pixeldata
        self.sz_compressed_pixeldata = sz_compressed_pixeldata
        self.pos_compressed_pixeldata = pos_compressed_pixeldata
        self.bitpos_in_compressed_pixeldata = bitpos_in_compressed_pixeldata
        self.nb_components = nb_components
        self.components_data = components_data
        self.huff_tables1 = huff_tables1
        self.huff_tables2 = huff_tables2
        self.quant_table = quant_table
        self.pixel_Y = pixel_Y
        self.pixel_Cb = pixel_Cb
        self.pixel_Cr = pixel_Cr


fn open_new_picture(name: String, inout picture: picture_t) raises:
    let _os = Python.import_module("os")

    picture.filesize = _os.path.getsize(name).__index__()

    var f = open(name, "rb")
    let data = f.read()
    f.close()
    picture.stringdata = data
    for i in range(len(data)):
        let x: UInt8
        x = UInt8(ord(data[i]))

        let y: UInt16
        y = UInt16(ord(data[i]))
        picture.data.push_back(x)
        picture.helpdata.push_back(y)

    picture.pos_in_file = 0
    picture.nb_components = 0

    for i in range(2):
        let huffmanEntryArray1 = HuffmanEntryArray(256)
        let huffmanEntryArray2 = HuffmanEntryArray(256)
        for j in range(256):
            huffmanEntryArray1.data.store(j, HuffmanEntry(0, 0, 0))
            huffmanEntryArray2.data.store(j, HuffmanEntry(0, 0, 0))
        let huffmanTable1 = HuffmanTable(0, huffmanEntryArray1)
        let huffmanTable2 = HuffmanTable(0, huffmanEntryArray2)
        picture.huff_tables1.data.store(i, huffmanTable1)
        picture.huff_tables2.data.store(i, huffmanTable2)
    print(picture.filesize, " bytes read from ", name + "\n")



fn get_marker(data: DynamicVector[UInt16], pos: Int) -> UInt16:
    return (data[pos] << 8) | data[pos + 1]


fn get1i(data: DynamicVector[UInt8],inout pos: Int) -> UInt16:
    let val:UInt16 = data[pos].cast[DType.uint16]()
    pos += 1
    return val

fn get2i(data: DynamicVector[UInt8],inout pos: Int) -> UInt16:
    let val:UInt16 = (data[pos].cast[DType.uint16]() << 8 | data[pos+1].cast[DType.uint16]()).cast[DType.uint16]()
    pos += 2
    return val

fn skip_EXIF(inout picture: picture_t) raises:
    let len_val:UInt16 = get2i(picture.data, picture.pos_in_file)
    print("APP1 (probably EXIF) found (length", len_val, "bytes), skipping")
    picture.pos_in_file += int(len_val) -2

fn parse_APP0(inout picture: picture_t) raises:
    let len_val:UInt16 = get2i(picture.data, picture.pos_in_file)
    print("APP0 found (length" ,len_val, "bytes)")
    if len_val < 16:
        raise Error("APP0: too short")
    let identifier:String = picture.stringdata[picture.pos_in_file: picture.pos_in_file + 5]

    picture.pos_in_file += 5

    let version_major:UInt16 = get1i(picture.data, picture.pos_in_file)
    let version_minor:UInt16 = get1i(picture.data, picture.pos_in_file)
    let units:UInt16 = get1i(picture.data, picture.pos_in_file)
    let Xdensity:UInt16 = get2i(picture.data, picture.pos_in_file)
    let Ydensity:UInt16 = get2i(picture.data, picture.pos_in_file)
    let Xthumbnail:UInt16 = get1i(picture.data, picture.pos_in_file)
    let Ythumbnail:UInt16 = get1i(picture.data, picture.pos_in_file)

    if identifier != "JFIF\x00":
        print("Invalid identifier: ", identifier)
        raise Error("APP0: invalid identifier")
    
    print("version",  version_major, ".", version_minor)
    print("units" ,units)
    print("density X", Xdensity, "Y", Ydensity)
    
    let bytes_thumbnail = 3 * Xthumbnail * Ythumbnail
    print("bytes_thumbnail:", bytes_thumbnail)
    
    if bytes_thumbnail !=0:
        print("thumbnail" ,bytes_thumbnail, "bytes, skipping")
        picture.pos_in_file += int(bytes_thumbnail)
    else:
        print("no thumbnail")

fn parse_DQT(inout picture: picture_t) raises:
    let Lq:UInt16 = get2i(picture.data, picture.pos_in_file)
    print("DQT found (length", Lq, "bytes)")

    let PqTq:UInt8 = get1i(picture.data, picture.pos_in_file).cast[DType.uint8]()

    let Pq:UInt8 = (PqTq >> 4) & 0x0f
    let Tq:UInt8 = PqTq & 0x0f
    if Pq == 0:
        print("Pq (element precision)", Pq, "->" ,"8 bits")
    else:
        print("Pq (element precision)", Pq, "->" ,"8 bits")
    print("Tq (table destination identifier)", Tq)
    if Pq != 0:
        raise Error("DQT: only 8 bit precision supported")
    
    let nb_data_bytes:UInt16 = Lq - 2 - 1
    
    if nb_data_bytes != 64:
        raise Error("DQT: nb_data_bytes != 64")
    
    for u in range(8):
        for v in range(8):
            let Q:UInt8 = get1i(picture.data, picture.pos_in_file).cast[DType.uint8]()
            picture.quant_table.__setitem__(Tq,u,v,Q)


fn ceil_to_multiple_of(val: SIMD[DType.uint32, 1], multiple: SIMD[DType.uint32, 1]) -> SIMD[DType.uint32, 1]:
    let result = multiple * ((val + multiple - 1) // multiple)
    return result

fn parse_SOF0(inout picture: picture_t, comp_names: DynamicVector[String]) raises:
    let len_val:UInt16 = get2i(picture.data, picture.pos_in_file)
    print("SOF0 found (length", len_val, "bytes)")
    let P:UInt32 = get1i(picture.data, picture.pos_in_file).cast[DType.uint32]()
    let Y:UInt32 = get2i(picture.data, picture.pos_in_file).cast[DType.uint32]()
    let X:UInt32 = get2i(picture.data, picture.pos_in_file).cast[DType.uint32]()
    let Nf:UInt32 = get1i(picture.data, picture.pos_in_file).cast[DType.uint32]()

    if P != 8:
        raise Error("SOF0: P != 8 unsupported")

    if Y == 0:
        raise Error("SOF0: Y == 0 unsupported")

    print("P", P, "(must be 8)")
    print("imagesize X" ,X, "Y", Y)
    print("Nf (number of components)", Nf)

    if Nf != 3:
        raise Error("picture does not have 3 components, this code will not work")
    
    picture.size_X = X
    picture.size_Y = Y

    for i in range(Nf):
        let C:UInt32 = get1i(picture.data, picture.pos_in_file).cast[DType.uint32]()
        let HV:UInt32 = get1i(picture.data, picture.pos_in_file).cast[DType.uint32]()
        let H:UInt32 = (HV >> 4) & 0x0F
        let V:UInt32 = HV & 0x0F
        let Tq:UInt32 = get1i(picture.data, picture.pos_in_file).cast[DType.uint32]()
        
        let h = ComponentsData(0,0,0,0,0,0,0)
        picture.components_data.push_back(h)
        picture.components_data[i].H = H
        picture.components_data[i].V = V
        picture.components_data[i].Tq = Tq

        print("component", i, comp_names[i], "C", C, "H", H, "V", V, "Tq", Tq)

    picture.nb_components = Nf

    var Hmax:UInt32 = 0
    var Vmax:UInt32 = 0

    for i in range(picture.nb_components):
        if picture.components_data[i].H > Hmax:
            Hmax = picture.components_data[i].H
        if picture.components_data[i].V > Vmax:
            Vmax = picture.components_data[i].V

    picture.Hmax = Hmax
    picture.Vmax = Vmax

    let temp_Hmax: UInt32 = Hmax.cast[DType.uint32]()
    let temp_Vmax: UInt32 = Vmax.cast[DType.uint32]()

    picture.nb_MCU_total = (ceil_to_multiple_of(picture.size_X, 8 * temp_Hmax) // (8 * temp_Hmax)) * \
                       (ceil_to_multiple_of(picture.size_Y, 8 * temp_Hmax) // (8 * temp_Vmax))
    

    print("Hmax", Hmax, "Vmax", Vmax)
    print("MCU_total", picture.nb_MCU_total)

    for i in range(picture.nb_components):
        let xi =((picture.size_X * (picture.components_data[i].H)) / Hmax + 0.5)
        let yi = (picture.size_Y * picture.components_data[i].V) / Vmax + 0.5

        picture.components_data[i].xi = xi
        picture.components_data[i].yi = yi

        print("component", i, comp_names[i], "xi", xi, "yi", yi)
    
    print("allocating memory for pixels")
    picture.pixel_Y = Array2D(X.to_int(), Y.to_int())
    picture.pixel_Cb = Array2D(X.to_int(), Y.to_int())
    picture.pixel_Cr = Array2D(X.to_int(), Y.to_int())
    for x in range(picture.size_X):
        for y in range(picture.size_Y):
            picture.pixel_Y.__setitem__(x,y,0.0)
            picture.pixel_Cb.__setitem__(x,y,0.0)
            picture.pixel_Cr.__setitem__(x,y,0.0)
    print("memory allocated")

fn parse_DHT(inout picture: picture_t) raises:

    let len:UInt16 = get2i(picture.data, picture.pos_in_file)
    print("DHT found (length", len, "bytes)")

    let TcTh:UInt8 = get1i(picture.data, picture.pos_in_file).cast[DType.uint8]()
    let Tc:UInt8 = (TcTh >> 4) & 0x0f
    let Th:UInt8 = TcTh & 0x0f

    if Tc == 0:
        print("Tc", Tc, "DC Table")
    else:
        print("Tc", Tc, "AC Table")
    print("Th (table destination identifier)" ,Th)

    var L = Array1D(16,0)
    var mt:UInt8 = 0
    for i in range(16):
        let a:UInt8 = get1i(picture.data, picture.pos_in_file).cast[DType.uint8]()
        L.__setitem__(i,a)
        mt += L.__getitem__(i)

    print("total" ,mt, "codes")

    var codeword: UInt16 = 0


    for i in range(16):
        for j in range(L.__getitem__(i)):
            let V:UInt8 = get1i(picture.data, picture.pos_in_file).cast[DType.uint8]()
            if Tc == 0:
                var huffmanTable1 = picture.huff_tables1.data.load(Th.to_int())

                var huffmanEntry1 = huffmanTable1.entries.data.load(huffmanTable1.nb_entries)
                huffmanEntry1.sz = i+1
                huffmanEntry1.codeword = codeword.to_int()
                huffmanEntry1.decoded = V.to_int()
                huffmanTable1.entries.data.store(huffmanTable1.nb_entries, huffmanEntry1)

                huffmanTable1.nb_entries +=1
                picture.huff_tables1.data.store(Th.to_int(), huffmanTable1)


            elif Tc == 1:

                var huffmanTable2 = picture.huff_tables2.data.load(Th.to_int())
                var huffmanEntry2 = huffmanTable2.entries.data.load(huffmanTable2.nb_entries)
                huffmanEntry2.sz = i+1
                huffmanEntry2.codeword = codeword.to_int()
                huffmanEntry2.decoded = V.to_int()
                huffmanTable2.entries.data.store(huffmanTable2.nb_entries, huffmanEntry2)

                huffmanTable2.nb_entries +=1
                picture.huff_tables2.data.store(Th.to_int(), huffmanTable2)

            codeword += 1
        codeword <<= 1

fn parse_SOS(inout picture: picture_t, comp_names: DynamicVector[String]) raises:
    let len:UInt16 = get2i(picture.data, picture.pos_in_file)
    print("SOS found (length", len, "bytes)")

    let Ns:UInt8 = get1i(picture.data, picture.pos_in_file).cast[DType.uint8]()
    print("Ns", Ns)

    for j in range(Ns):
        let Cs:UInt8 = get1i(picture.data, picture.pos_in_file).cast[DType.uint8]()
        let TdTa:UInt8 = get1i(picture.data, picture.pos_in_file).cast[DType.uint8]()
        let Td:UInt8 = (TdTa >> 4) & 0x0f
        let Ta:UInt8 = TdTa & 0x0f

        print("component", j, comp_names[j], "Cs", Cs, "Td", Td, "Ta", Ta)
        picture.components_data[j].Td = Td.to_int()  # DC
        picture.components_data[j].Ta = Ta.to_int()  # AC

    let Ss:UInt8 = get1i(picture.data, picture.pos_in_file).cast[DType.uint8]()
    let Se:UInt8 = get1i(picture.data, picture.pos_in_file).cast[DType.uint8]()
    let AhAl:UInt8 = get1i(picture.data, picture.pos_in_file).cast[DType.uint8]()
    let Ah:UInt8 = (AhAl >> 4) & 0x0f
    let Al:UInt8 = AhAl & 0x0f

    print("Ss", Ss, "Se", Se, "Ah", Ah, "Al", Al)

    picture.pos_compressed_pixeldata = picture.pos_in_file

    print("compressed pixeldata starts at pos", picture.pos_compressed_pixeldata)


fn copy_bitmap_data_remove_stuffing(inout picture: picture_t) raises:
    print("removing stuffing...")
    var pos:Int = picture.pos_compressed_pixeldata
    var size:Int = 0
    var byte:UInt8 = 0
    var combined:UInt16 = 0
    while combined != 0xFFD9:
        if pos >= picture.filesize:
            raise Error("marker EOI (0xFFD9) missing")
        
        byte = picture.data[pos]
        pos += 1

        if byte == 0xFF:
            let byte2:UInt8 = picture.data[pos]
            pos += 1

            if byte2 != 0x00:
                combined = (byte.cast[DType.uint16]() << 8) | byte2.cast[DType.uint16]()
            else:
                size += 1
        else:
            size += 1

    let size_stuffed:Int = pos - picture.pos_compressed_pixeldata - 2

    for i in range(size):
        picture.compressed_pixeldata.push_back(0)
    
    print(size_stuffed, "bytes with stuffing")

    var i:Int = picture.pos_compressed_pixeldata
    pos = 0
    var size_without_stuffing:Int = 0

    while i < picture.pos_compressed_pixeldata + size_stuffed:
        if picture.data[i] != 0xFF:
            picture.compressed_pixeldata[pos] = picture.data[i].to_int()
            pos += 1
            size_without_stuffing += 1
            i += 1
        elif picture.data[i] == 0xFF and picture.data[i + 1] == 0x00:
            picture.compressed_pixeldata[pos] = 0xFF
            pos += 1
            size_without_stuffing += 1
            i += 2
        else:
            raise Error("unexpected marker found in bitstream")

    picture.bitpos_in_compressed_pixeldata = 0
    picture.sz_compressed_pixeldata = size_without_stuffing
    picture.pos_in_file = picture.pos_compressed_pixeldata + size_stuffed

    print(size_without_stuffing, "data bytes without stuffing\n")


fn convert_to_neg(bits: UInt16, sz: UInt8) -> Int16:
    let ret: Int16 = -((bits.cast[DType.int16]() ^ 0xFFFF) & ((1 << sz.cast[DType.int16]()) - 1))
    return ret

fn bitstream_get_bits(inout picture: picture_t, nb_bits: UInt32) raises -> UInt16:
    if nb_bits > 16:
        raise Error("bitstream_get_bits: >16 bits requested")

    var index:Int = picture.bitpos_in_compressed_pixeldata // 8
    var pos_in_byte:Int = 7 - picture.bitpos_in_compressed_pixeldata % 8
    var ret:UInt16 = 0
    var bits_copied:UInt32 = 0

    while pos_in_byte >= 0 and bits_copied < nb_bits:
        ret <<= 1
        let temp = picture.compressed_pixeldata[index] & (1 << pos_in_byte)
        if temp > 0:
            ret |= 1
        else:
            ret |= 0

        bits_copied += 1
        pos_in_byte -= 1
        if pos_in_byte < 0:
            pos_in_byte = 7
            index += 1

    return ret

fn bitstream_remove_bits(inout picture: picture_t, nb_bits: UInt32) raises:
    picture.bitpos_in_compressed_pixeldata += nb_bits.to_int()

fn huff_decode(inout picture: picture_t, Tc: UInt8, Th: UInt8, sz: UInt8, bitstream: UInt16, inout decoded: UInt8)  raises ->Bool:
    if Tc == 0:
        for i in range(picture.huff_tables1.data.load(Th.to_int()).nb_entries):
            if (picture.huff_tables1.data.load(Th.to_int()).entries.data.load(i).sz == sz.to_int() and
                picture.huff_tables1.data.load(Th.to_int()).entries.data.load(i).codeword == bitstream.to_int()):

                decoded = picture.huff_tables1.data.load(Th.to_int()).entries.data.load(i).decoded
                return True
    elif Tc == 1:
        for i in range(picture.huff_tables2.data.load(Th.to_int()).nb_entries):
            if (picture.huff_tables2.data.load(Th.to_int()).entries.data.load(i).sz == sz.to_int() and
                picture.huff_tables2.data.load(Th.to_int()).entries.data.load(i).codeword == bitstream.to_int()):

                decoded = picture.huff_tables2.data.load(Th.to_int()).entries.data.load(i).decoded
                return True

    return False

fn bitstream_get_next_decoded_element(inout picture: picture_t, Tc: UInt8, Th: UInt8,inout decoded: UInt8, inout nb_bits: UInt32)  raises ->Bool:
    var huff_candidate: UInt16 = 0
    var found:Bool = False
    while picture.bitpos_in_compressed_pixeldata < 8 * picture.sz_compressed_pixeldata:
        found = False
        for num_bits in range(1, 17):
            nb_bits = num_bits
            if picture.bitpos_in_compressed_pixeldata + nb_bits > 8 * picture.sz_compressed_pixeldata:
                raise Error("end of stream, requested too many bits")
            
            huff_candidate = bitstream_get_bits(picture, nb_bits)
            let boo:Bool = huff_decode(picture, Tc, Th, nb_bits.cast[DType.uint8](), huff_candidate, decoded)

            if boo:
                found = True
                bitstream_remove_bits(picture, nb_bits)
                return True
        if not found:
            var is_all_one:Bool = True
            for i in range(nb_bits - 1):
                if (huff_candidate & (1 << i)) == 0:
                    is_all_one = False
                    break
            
            if is_all_one:
                bitstream_remove_bits(picture, nb_bits)
            else:
                raise Error("unknown code in bitstream bitpos")
    return False

fn reverse_ZZ_and_dequant(inout picture: picture_t, quant_table: UInt8, inp: Array2D, inout outp: Array2D):
    var reverse_ZZ_u = Array2D(8, 8)
    let vector = SIMD[DType.float32, 64] (0, 0, 1, 2, 1, 0, 0, 1, 2, 3, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 
                                          4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 4, 5, 6, 7, 7, 6, 5, 6, 7, 7)
    var counter:Int = 0

    for i in range(8):
        for j in range(8):
            reverse_ZZ_u.__setitem__(i,j,vector[counter])
            counter+=1

    var reverse_ZZ_v = Array2D(8, 8)
    let vector1 = SIMD[DType.float32, 64] (0, 1, 0, 0, 1, 2, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 4,
                                           3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 5, 6, 7, 7, 6, 7)
    counter = 0

    for i in range(8):
        for j in range(8):
            reverse_ZZ_v.__setitem__(i,j,vector1[counter])
            counter+=1
    
    for u in range(8):
        for v in range(8):
            let a = inp.__getitem__(u,v) * (picture.quant_table.__getitem__(quant_table.to_int(),u,v).cast[DType.float32] ())
            outp.__setitem__(reverse_ZZ_u.__getitem__(u,v).to_int(),reverse_ZZ_v.__getitem__(u,v).to_int(), a)


fn data_unit_do_idct(inp: Array2D, inout outp: Array2D, mat: Array2Dnew):
    var rxy: Float32 = 0
    for y in range(8):
        for x in range(8):
            rxy = 0
            for u in range(8):
                for v in range(8):
                    let Svu: Float32 = inp.__getitem__(v,u)
                    rxy += Svu * mat.__getitem__(x)[u] * mat.__getitem__(y)[v]

            rxy *= 0.25
            rxy += 128
            outp.__setitem__(x,y,rxy)

fn store_data_unit_YCbCr(inout picture: picture_t, MCU: UInt32, component: UInt32, data_unit: UInt32, data: Array2D) raises:
    let zoomX = picture.Hmax // picture.components_data[component.to_int()].H
    let zoomY = picture.Vmax // picture.components_data[component.to_int()].V

    let scaleX = 8 * picture.components_data[component.to_int()].H
    let scaleY = 8 * picture.components_data[component.to_int()].V

    let startX = MCU % (ceil_to_multiple_of(picture.size_X, 8 * picture.Hmax) // (scaleX * zoomX))
    let startY = MCU // (ceil_to_multiple_of(picture.size_X, 8 * picture.Hmax) // (scaleY * zoomY))

    let startHiX = data_unit % picture.components_data[component.to_int()].H
    let startHiY = data_unit // picture.components_data[component.to_int()].H

    for x in range(8):
        for y in range(8):
            for zx in range(zoomX):
                for zy in range(zoomY):
                    let posX = (scaleX * startX + 8 * startHiX + x) * zoomX + zx
                    let posY = (scaleY * startY + 8 * startHiY + y) * zoomY + zy

                    if posX < picture.size_X and posY < picture.size_Y:
                        if component == 0:
                            picture.pixel_Y.__setitem__(posX.to_int(), posY.to_int(), data.__getitem__(x,y))
                        elif component == 1:
                            picture.pixel_Cb.__setitem__(posX.to_int(), posY.to_int(), data.__getitem__(x,y))
                        elif component == 2:
                            picture.pixel_Cr.__setitem__(posX.to_int(), posY.to_int(), data.__getitem__(x,y))
                        else:
                            raise Error("unknown component")

fn parse_bitmap_data(inout picture: picture_t , mat: Array2Dnew) raises:
    print("parsing bitstream...")

    var nb_bits:UInt32 = 0
    var component:UInt32 = 0
    var data_unit:UInt32 = 0
    let ac_count:UInt32 = 0

    var precedent_DC = Array1Dnew(4,0)

    precedent_DC.__setitem__(0,0)
    precedent_DC.__setitem__(1,0)
    precedent_DC.__setitem__(2,0)
    precedent_DC.__setitem__(3,0)

    var nb_MCU:UInt32 = 0

    var matrix = Array2D(8,8)

    for i in range(8):
        for j in range(8):
            matrix.__setitem__(i,j,0)
    while nb_MCU < picture.nb_MCU_total:
        component = 0
        while component < picture.nb_components:
            data_unit = 0
            while data_unit < (picture.components_data[component.to_int()].V * picture.components_data[component.to_int()].H):
                for u in range(8):
                    for v in range(8):
                        matrix.__setitem__(u,v,0)
                
                var SSSS:UInt8 = 0
                var DC:Int16 = 0
                let boo: Bool = bitstream_get_next_decoded_element(picture, 0, picture.components_data[component.to_int()].Td, SSSS, nb_bits)

                if not boo:
                     raise Error("no DC data")
                if SSSS != 0:
                    let bits_DC: UInt16 = bitstream_get_bits(picture, SSSS.cast[DType.uint32]())
                    bitstream_remove_bits(picture, SSSS.cast[DType.uint32]())
                    let msb_DC:UInt32 = (bits_DC.cast[DType.uint32]() & (1 << (SSSS.cast[DType.uint32]() - 1)).cast[DType.uint32]())
                    
                    if msb_DC > 0:
                        DC = precedent_DC.__getitem__(component.to_int()) + bits_DC.cast[DType.int16]()

                    else:
                        DC = (precedent_DC.__getitem__(component.to_int()) + convert_to_neg(bits_DC, SSSS))
                        
                else:
                    DC = (precedent_DC.__getitem__(component.to_int()) + 0)
                
                matrix.__setitem__(0,0,DC.cast[DType.float32] ())
                precedent_DC.__setitem__(component.to_int(),DC)

                var AC: Int16 = 0
                var ac_count: Int16 = 0
                while ac_count < 63:
                    var RRRRSSSS: UInt8 = 0
                    let boo: Bool = bitstream_get_next_decoded_element(picture, 1, picture.components_data[component.to_int()].Ta, RRRRSSSS, nb_bits)
                    if not boo:
                        raise Error("no DC data")
                    let RRRR: UInt8 = RRRRSSSS >> 4
                    SSSS = RRRRSSSS & 0x0f
                    if RRRR == 0 and SSSS == 0:
                        break
                    elif RRRR == 0x0F and SSSS == 0:
                        ac_count += 16
                    else:
                        ac_count += RRRR.cast[DType.int16]()

                        let bits_AC: UInt16 = bitstream_get_bits(picture, SSSS.cast[DType.uint32]())
                        bitstream_remove_bits(picture, SSSS.cast[DType.uint32]())

                        let msb_AC = (bits_AC & (1 << (SSSS - 1)).cast[DType.uint16]())
                        if msb_AC > 0:
                            AC = bits_AC.cast[DType.int16]()
                        else:
                            AC = convert_to_neg(bits_AC, SSSS)

                        let u = (ac_count + 1) // 8
                        let v = (ac_count + 1) % 8

                        matrix.__setitem__(u.to_int(),v.to_int(),AC.cast[DType.float32] ())
                        ac_count += 1

                var matrix_dequant = Array2D(8,8)
                for i in range(8):
                    for j in range(8):
                        matrix_dequant.__setitem__(i,j,0)
                
                reverse_ZZ_and_dequant(picture, picture.components_data[component.to_int()].Tq.cast[DType.uint8] (), matrix, matrix_dequant)

                var matrix_decoded = Array2D(8,8)
                for i in range(8):
                    for j in range(8):
                        matrix_decoded.__setitem__(i,j,0)
                
                data_unit_do_idct(matrix_dequant, matrix_decoded, mat)
                store_data_unit_YCbCr(picture, nb_MCU, component, data_unit, matrix_decoded)

                data_unit += 1
            component += 1
        nb_MCU += 1

    print("parsed MCU", nb_MCU)

fn parse_picture(inout picture: picture_t, comp_names: DynamicVector[String], mat: Array2Dnew) raises:
    while picture.pos_in_file <= picture.filesize - 1:
        let marker: UInt16
        marker = get_marker(picture.helpdata, picture.pos_in_file)
        picture.pos_in_file+=2
        print("marker:",marker)
        if marker == 0xFFD8:
            print("SOI found")
        elif marker == 0xFFE1:
            skip_EXIF(picture)
        elif marker == 0xFFE0:
            parse_APP0(picture)
        elif marker == 0xFFDB:
            parse_DQT(picture)
        elif marker == 0xFFC0:
            parse_SOF0(picture, comp_names)
        elif marker == 0xFFC4:
            parse_DHT(picture)
        elif marker == 0xFFDA:
            parse_SOS(picture, comp_names)
            copy_bitmap_data_remove_stuffing(picture)
            parse_bitmap_data(picture, mat)

        else:
            break

fn clamp(v: Float32) ->UInt8:
    if v < 0:
        return 0
    if v > 255:
        return 255
    return v.cast[DType.uint8] ()

fn write_ppm(inout picture: picture_t, filename:String) raises:
    print("writing file", filename)
    with open(filename, "w") as out:
        out.write("P3\n" + str(picture.size_X) + " " + str(picture.size_Y) + "\n255\n")
        for y in range(picture.size_Y):
            for x in range(picture.size_X):
                let Y = picture.pixel_Y.__getitem__(x,y)
                let Cb = picture.pixel_Cb.__getitem__(x,y)
                let Cr = picture.pixel_Cr.__getitem__(x,y)
                let r = Y + 1.402 * (Cr - 128)
                let g = Y - (0.114 * 1.772 * (Cb - 128) + 0.299 * 1.402 * (Cr - 128)) / 0.587
                let b = Y + 1.772 * (Cb - 128)
                out.write(str(clamp(math.round(r))) + " " + str(clamp(math.round(g))) + " " + str(clamp(math.round(b))) + " ")
            out.write("\n")
    
    print("output file written\n")

fn main() raises:

    let start_time = now()
    var comp_names =  DynamicVector[String] ()
    comp_names.push_back("Y")
    comp_names.push_back("Cb")
    comp_names.push_back("Cr")
    let data = DynamicVector[UInt8] ()
    let helpdata = DynamicVector[UInt16] ()
    let stringdata = ""
    let filesize = 0
    let pos_in_file = 0
    let size_X = 0
    let size_Y = 0
    let Hmax = 0
    let Vmax = 0
    let nb_MCU_total = 0
    let compressed_pixeldata = DynamicVector[Int] ()
    let sz_compressed_pixeldata = 0
    let pos_compressed_pixeldata = 0
    let bitpos_in_compressed_pixeldata = 0
    let nb_components = 0
    let components_data = DynamicVector[ComponentsData] ()
    let quant_table = Array3D(4,8,8)
    let pixel_Y = Array2D(1000,1000)
    let pixel_Cb = Array2D(1000,1000)
    let pixel_Cr = Array2D(1000,1000)
    let huff_tables1 = HuffmanTableArray(2)
    let huff_tables2 = HuffmanTableArray(2)

    let a_c : Float32 = 0.9807
    let b_c : Float32 = 0.8314
    let c_c : Float32 = 0.5555
    let d_c : Float32 = 0.1950
    let e_c : Float32 = 0.9238
    let f_c : Float32 = 0.3826
    let g_c : Float32 = 0.7071
    var mat = Array2Dnew(8)
    mat.__setitem__(0,SIMD[DType.float32, 8] (0.7071, a_c,  e_c,  b_c,  g_c,  c_c,  f_c,  d_c))
    mat.__setitem__(1,SIMD[DType.float32, 8] (0.7071, b_c,  f_c, -d_c, -g_c, -a_c, -e_c, -c_c))
    mat.__setitem__(2,SIMD[DType.float32, 8] (0.7071, c_c, -f_c, -a_c, -g_c,  d_c,  e_c,  b_c))
    mat.__setitem__(3,SIMD[DType.float32, 8] (0.7071, d_c, -e_c, -c_c,  g_c,  b_c, -f_c, -a_c))
    mat.__setitem__(4,SIMD[DType.float32, 8] (0.7071, -d_c, -e_c,  c_c,  g_c, -b_c, -f_c,  a_c))
    mat.__setitem__(5,SIMD[DType.float32, 8] (0.7071, -c_c, -f_c,  a_c, -g_c, -d_c,  e_c, -b_c))
    mat.__setitem__(6,SIMD[DType.float32, 8] (0.7071, -b_c,  f_c,  d_c, -g_c,  a_c, -e_c,  c_c))
    mat.__setitem__(7,SIMD[DType.float32, 8] (0.7071, -a_c,  e_c, -b_c,  g_c, -c_c,  f_c, -d_c))


    var pic = picture_t(data, helpdata, stringdata,filesize, pos_in_file, size_X, size_Y, Hmax, Vmax, nb_MCU_total, compressed_pixeldata, sz_compressed_pixeldata, pos_compressed_pixeldata, bitpos_in_compressed_pixeldata, nb_components, components_data, huff_tables1, huff_tables2, quant_table, pixel_Y, pixel_Cb, pixel_Cr)

    open_new_picture("../Example_images/test1.jpg", pic)
    parse_picture(pic, comp_names, mat)
    let end_time = now()
    let current_directory = path.cwd()
    write_ppm(pic, str(current_directory) + "/decodedimage.ppm")

    let write_time = now()

    let execution_time_algo : Float32 = end_time - start_time
    let execution_time_write : Float32 = write_time - end_time
    
    let execution_time_seconds_algo :  Float32 = execution_time_algo / 1000000000
    let execution_time_seconds_write :  Float32 = execution_time_write / 1000000000
    
    print("Time taken by the Jpeg decoder algorithm:", execution_time_seconds_algo, "seconds")
    print("Time taken for writing the image:", execution_time_seconds_write, "seconds")


--- Python/README.md ---

# Usage
```python3 main.py filename.jpg```

It will create a decoded ppm file named decodedimage.ppm


--- Python/main.py ---

import os
import math
import time
import sys

comp_names = ["Y", "Cb", "Cr"]

class ComponentsData:
    def __init__(self):
        self.H = 0
        self.V = 0
        self.Tq = 0
        self.xi = 0
        self.yi = 0
        self.Td = 0  # quant table for DC
        self.Ta = 0  # quant table for AC

class HuffmanEntry:
    def __init__(self):
        self.sz = 0
        self.codeword = 0
        self.decoded = 0

class HuffmanTable:
    def __init__(self):
        self.nb_entries = 0
        self.entries = [HuffmanEntry() for _ in range(256)]

class PixelYCbCr:
    def __init__(self):
        self.Y = 0
        self.Cb = 0
        self.Cr = 0
        
class Picture:
    def __init__(self):
        self.data = bytearray()
        self.filesize = 0
        self.pos_in_file = 0
        self.size_X = 0
        self.size_Y = 0
        self.Hmax = 0
        self.Vmax = 0
        self.nb_MCU_total = 0
        self.compressed_pixeldata = bytearray()
        self.sz_compressed_pixeldata = 0
        self.pos_compressed_pixeldata = 0
        self.bitpos_in_compressed_pixeldata = 0
        self.nb_components = 0
        self.components_data = [ComponentsData() for _ in range(4)]
        self.huff_tables = [[HuffmanTable() for _ in range(2)] for _ in range(2)]
        self.quant_tables = [[[0 for _ in range(8)] for _ in range(8)] for _ in range(4)]
        self.pixels_YCbCr = [[PixelYCbCr() for _ in range(self.size_Y)] for _ in range(self.size_X)]

matrix8x8_t = [[0.0 for _ in range(8)] for _ in range(8)]

def get1i(data, pos):
    val = data[pos]
    pos += 1
    return val, pos

def get2i(data, pos):
    val = (data[pos] << 8) | data[pos + 1]
    pos += 2
    return val, pos

def get4i(data, pos):
    val = (data[pos[0]] << 24) | (data[pos[0] + 1] << 16) | (data[pos[0] + 2] << 8) | data[pos[0] + 3]
    pos[0] += 4
    return val


def get_marker(data, pos):
    return (data[pos] << 8) | data[pos + 1]

def to_bin(word, sz):
    str_val = ''
    for i in range(sz):
        str_val += '0' + str(int(bool(word & (1 << (sz - i - 1)))))
    return str_val

def ceil_to_multiple_of(val, multiple):
    return multiple * ((val + multiple - 1) // multiple)

def skip_EXIF(pic):
    len_val, pic.pos_in_file = get2i(pic.data, pic.pos_in_file)
    print(f"APP1 (probably EXIF) found (length {len_val} bytes), skipping")
    pic.pos_in_file += len_val - 2


def parse_APP0(pic):
    len_val, pic.pos_in_file = get2i(pic.data, pic.pos_in_file)
    print(f"APP0 found (length {len_val} bytes)")
    if len_val < 16:
        raise ValueError("APP0: too short")
    
    identifier = pic.data[pic.pos_in_file: pic.pos_in_file + 5].decode('utf-8')

    pic.pos_in_file += 5
    
    version_major, pic.pos_in_file  = get1i(pic.data, pic.pos_in_file)
    version_minor, pic.pos_in_file = get1i(pic.data, pic.pos_in_file)
    units, pic.pos_in_file = get1i(pic.data, pic.pos_in_file)
    Xdensity, pic.pos_in_file = get2i(pic.data, pic.pos_in_file)
    Ydensity, pic.pos_in_file = get2i(pic.data, pic.pos_in_file)
    Xthumbnail, pic.pos_in_file = get1i(pic.data, pic.pos_in_file)
    Ythumbnail, pic.pos_in_file = get1i(pic.data, pic.pos_in_file)
    
    if identifier != "JFIF\x00":
        print(f"Invalid identifier: {identifier}")
        raise ValueError("APP0: invalid identifier")
    
    print(f"version {version_major}.{version_minor}")
    print(f"units {units}")
    print(f"density X {Xdensity} Y {Ydensity}")
    
    bytes_thumbnail = 3 * Xthumbnail * Ythumbnail
    
    if bytes_thumbnail:
        print(f"thumbnail {bytes_thumbnail} bytes, skipping")
        pic.pos_in_file += bytes_thumbnail
    else:
        print("no thumbnail")

def parse_DQT(pic):
    Lq, pic.pos_in_file = get2i(pic.data, pic.pos_in_file)
    print(f"DQT found (length {Lq} bytes)")
    
    PqTq, pic.pos_in_file = get1i(pic.data, pic.pos_in_file)
    Pq = (PqTq >> 4) & 0x0f
    Tq = PqTq & 0x0f
    print(f"Pq (element precision) {Pq} -> {8 if Pq == 0 else 16} bits")
    print(f"Tq (table destination identifier) {Tq}")
    
    if Pq != 0:
        raise ValueError("DQT: only 8 bit precision supported")
    
    nb_data_bytes = Lq - 2 - 1
    
    if nb_data_bytes != 64:
        raise ValueError("DQT: nb_data_bytes != 64")
    
    for u in range(8):
        for v in range(8):
            Q, pic.pos_in_file = get1i(pic.data, pic.pos_in_file)
            pic.quant_tables[Tq][u][v] = Q

def parse_SOF0(pic):
    len_val, pic.pos_in_file = get2i(pic.data, pic.pos_in_file)
    print(f"SOF0 found (length {len_val} bytes)")

    P, pic.pos_in_file = get1i(pic.data, pic.pos_in_file)
    Y, pic.pos_in_file = get2i(pic.data, pic.pos_in_file)
    X, pic.pos_in_file = get2i(pic.data, pic.pos_in_file)
    Nf, pic.pos_in_file = get1i(pic.data, pic.pos_in_file)

    if P != 8:
        raise ValueError("SOF0: P != 8 unsupported")

    if Y == 0:
        raise ValueError("SOF0: Y == 0 unsupported")

    print(f"P {P} (must be 8)")
    print(f"imagesize X {X} Y {Y}")
    print(f"Nf (number of components) {Nf}")

    if Nf != 3:
        raise ValueError("picture does not have 3 components, this code will not work")

    pic.size_X = X
    pic.size_Y = Y

    for i in range(Nf):
        C, pic.pos_in_file = get1i(pic.data, pic.pos_in_file)
        HV, pic.pos_in_file = get1i(pic.data, pic.pos_in_file)
        H = (HV >> 4) & 0x0F
        V = HV & 0x0F
        Tq, pic.pos_in_file = get1i(pic.data, pic.pos_in_file)

        pic.components_data[i].H = H
        pic.components_data[i].V = V
        pic.components_data[i].Tq = Tq

        print(f"component {i} ({comp_names[i]}) C {C}, H {H}, V {V}, Tq {Tq}")

    pic.nb_components = Nf

    Hmax = Vmax = 0

    for i in range(pic.nb_components):
        if pic.components_data[i].H > Hmax:
            Hmax = pic.components_data[i].H
        if pic.components_data[i].V > Vmax:
            Vmax = pic.components_data[i].V

    pic.Hmax = Hmax
    pic.Vmax = Vmax

    pic.nb_MCU_total = (ceil_to_multiple_of(pic.size_X, 8 * Hmax) // (8 * Hmax)) * \
                       (ceil_to_multiple_of(pic.size_Y, 8 * Hmax) // (8 * Vmax))

    print(f"Hmax {Hmax} Vmax {Vmax}")
    print(f"MCU_total {pic.nb_MCU_total}")

    for i in range(pic.nb_components):
        xi = int((pic.size_X * pic.components_data[i].H) / Hmax + 0.5)
        yi = int((pic.size_Y * pic.components_data[i].V) / Vmax + 0.5)

        pic.components_data[i].xi = xi
        pic.components_data[i].yi = yi

        print(f"component {i} ({comp_names[i]}) xi {xi} yi {yi}")

    print("allocating memory for pixels")

    pic.pixels_YCbCr = [
            [PixelYCbCr() for _ in range(pic.size_Y)]
            for _ in range(pic.size_X)
        ]

    print("memory allocated")


def parse_DHT(pic):
    len_val, pic.pos_in_file = get2i(pic.data, pic.pos_in_file)
    print(f"DHT found (length {len_val} bytes)")

    TcTh, pic.pos_in_file = get1i(pic.data, pic.pos_in_file)
    Tc = (TcTh >> 4) & 0x0f
    Th = TcTh & 0x0f

    print(f"Tc {Tc} ({'DC' if Tc == 0 else 'AC'} table)")
    print(f"Th (table destination identifier) {Th}")

    L = []
    mt = 0
    for i in range(16):
        a , pic.pos_in_file = get1i(pic.data, pic.pos_in_file)
        L.append(a)
        mt += L[i]

    print(f"total {mt} codes")

    codeword = 0
    for i in range(16):
        for j in range(L[i]):
            V, pic.pos_in_file = get1i(pic.data, pic.pos_in_file)
            pic.huff_tables[Tc][Th].entries[pic.huff_tables[Tc][Th].nb_entries].sz = i + 1
            pic.huff_tables[Tc][Th].entries[pic.huff_tables[Tc][Th].nb_entries].codeword = codeword
            pic.huff_tables[Tc][Th].entries[pic.huff_tables[Tc][Th].nb_entries].decoded = V
            pic.huff_tables[Tc][Th].nb_entries += 1
            codeword += 1
        codeword <<= 1


def parse_SOS(pic):
    len_val, pic.pos_in_file = get2i(pic.data, pic.pos_in_file)
    print(f"SOS found (length {len_val} bytes)")

    Ns, pic.pos_in_file = get1i(pic.data, pic.pos_in_file)
    print(f"Ns {Ns}")

    for j in range(Ns):
        Cs, pic.pos_in_file = get1i(pic.data, pic.pos_in_file)
        TdTa, pic.pos_in_file = get1i(pic.data, pic.pos_in_file)
        Td = (TdTa >> 4) & 0x0f
        Ta = TdTa & 0x0f

        print(f"component {j} ({comp_names[j]}) Cs {Cs} Td {Td} Ta {Ta}")
        pic.components_data[j].Td = Td  # DC
        pic.components_data[j].Ta = Ta  # AC

    Ss, pic.pos_in_file = get1i(pic.data, pic.pos_in_file)
    Se, pic.pos_in_file = get1i(pic.data, pic.pos_in_file)
    AhAl, pic.pos_in_file = get1i(pic.data, pic.pos_in_file)
    Ah = (AhAl >> 4) & 0x0f
    Al = AhAl & 0x0f

    print(f"Ss {Ss} Se {Se} Ah {Ah} Al {Al}")

    pic.pos_compressed_pixeldata = pic.pos_in_file

    print(f"compressed pixeldata starts at pos {pic.pos_compressed_pixeldata}\n")

def copy_bitmap_data_remove_stuffing(pic):
    print("removing stuffing...")

    # Get length of bitstream without stuffing
    pos = pic.pos_compressed_pixeldata
    size = 0
    combined = 0

    while combined != 0xFFD9:
        if pos >= pic.filesize:
            raise ValueError("marker EOI (0xFFD9) missing")

        byte = pic.data[pos]
        pos += 1

        if byte == 0xFF:
            byte2 = pic.data[pos]
            pos += 1

            if byte2 != 0x00:
                combined = (byte << 8) | byte2
            else:
                size += 1
        else:
            size += 1

    size_stuffed = pos - pic.pos_compressed_pixeldata - 2

    # Remove stuffing
    pic.compressed_pixeldata = [0] * size

    print(f"{size_stuffed} bytes with stuffing")

    i = pic.pos_compressed_pixeldata
    pos = 0
    size_without_stuffing = 0

    while i < pic.pos_compressed_pixeldata + size_stuffed:
        if pic.data[i] != 0xFF:
            pic.compressed_pixeldata[pos] = pic.data[i]
            pos += 1
            size_without_stuffing += 1
            i += 1
        elif pic.data[i] == 0xFF and pic.data[i + 1] == 0x00:
            pic.compressed_pixeldata[pos] = 0xFF
            pos += 1
            size_without_stuffing += 1
            i += 2
        else:
            raise ValueError(f"unexpected marker 0x{pic.data[i]:02x}{pic.data[i + 1]:02x} found in bitstream")

    pic.bitpos_in_compressed_pixeldata = 0
    pic.sz_compressed_pixeldata = size_without_stuffing
    pic.pos_in_file = pic.pos_compressed_pixeldata + size_stuffed

    print(f"{size_without_stuffing} data bytes without stuffing\n")


def convert_to_neg(bits, sz):
    ret = -((bits ^ 0xFFFF) & ((1 << sz) - 1))
    return ret

def bitstream_get_bits(pic, nb_bits):
    if nb_bits > 16:
        raise ValueError("bitstream_get_bits: >16 bits requested")

    index = pic.bitpos_in_compressed_pixeldata // 8
    pos_in_byte = 7 - pic.bitpos_in_compressed_pixeldata % 8
    ret = 0
    bits_copied = 0

    while pos_in_byte >= 0 and bits_copied < nb_bits:
        ret <<= 1
        ret |= int(bool(pic.compressed_pixeldata[index] & (1 << pos_in_byte)))
        bits_copied += 1
        pos_in_byte -= 1
        if pos_in_byte < 0:
            pos_in_byte = 7
            index += 1

    return ret

def bitstream_remove_bits(pic, nb_bits):
    pic.bitpos_in_compressed_pixeldata += nb_bits

def huff_decode(pic, Tc, Th, sz, bitstream, decoded):

    for i in range(pic.huff_tables[Tc][Th].nb_entries):
        if (pic.huff_tables[Tc][Th].entries[i].sz == sz and
                pic.huff_tables[Tc][Th].entries[i].codeword == bitstream):
            decoded = pic.huff_tables[Tc][Th].entries[i].decoded
            return True, decoded

    return False,decoded

def bitstream_get_next_decoded_element(pic, Tc, Th, decoded, nb_bits):
    huff_candidate = 0
    found = False
    
    while pic.bitpos_in_compressed_pixeldata < 8 * pic.sz_compressed_pixeldata:
        found = False
        for num_bits in range(1, 17):
            nb_bits = num_bits

            if pic.bitpos_in_compressed_pixeldata + nb_bits > 8 * pic.sz_compressed_pixeldata:
                raise ValueError("end of stream, requested too many bits")
            
            huff_candidate = bitstream_get_bits(pic, nb_bits)

            boo, decoded = huff_decode(pic, Tc, Th, nb_bits, huff_candidate, decoded)
            
            if boo:
                found = True
                bitstream_remove_bits(pic, nb_bits)
                return True, decoded, nb_bits
        
        if not found:
            is_all_one = True
            for i in range(nb_bits - 1):
                if (huff_candidate & (1 << i)) == 0:
                    is_all_one = False
                    break
            
            if is_all_one:
                bitstream_remove_bits(pic, nb_bits)
            else:
                raise ValueError(
                    f"unknown code in bitstream bitpos {pic.bitpos_in_compressed_pixeldata} byte 0x{pic.compressed_pixeldata[pic.bitpos_in_compressed_pixeldata // 8]:x} [prev 0x{pic.compressed_pixeldata[(pic.bitpos_in_compressed_pixeldata // 8) - 1]:x}, next 0x{pic.compressed_pixeldata[(pic.bitpos_in_compressed_pixeldata // 8) + 1]:x}]")
    return False, decoded, nb_bits


def store_data_unit_YCbCr(pic, MCU, component, data_unit, data):
    zoomX = pic.Hmax // pic.components_data[component].H
    zoomY = pic.Vmax // pic.components_data[component].V

    scaleX = 8 * pic.components_data[component].H
    scaleY = 8 * pic.components_data[component].V

    startX = MCU % (ceil_to_multiple_of(pic.size_X, 8 * pic.Hmax) // (scaleX * zoomX))
    startY = MCU // (ceil_to_multiple_of(pic.size_X, 8 * pic.Hmax) // (scaleY * zoomY))

    startHiX = data_unit % pic.components_data[component].H
    startHiY = data_unit // pic.components_data[component].H

    for x in range(8):
        for y in range(8):
            for zx in range(zoomX):
                for zy in range(zoomY):
                    posX = (scaleX * startX + 8 * startHiX + x) * zoomX + zx
                    posY = (scaleY * startY + 8 * startHiY + y) * zoomY + zy

                    if posX < pic.size_X and posY < pic.size_Y:
                        if component == 0:
                            pic.pixels_YCbCr[posX][posY].Y = data[x][y]
                        elif component == 1:
                            pic.pixels_YCbCr[posX][posY].Cb = data[x][y]
                        elif component == 2:
                            pic.pixels_YCbCr[posX][posY].Cr = data[x][y]
                        else:
                            raise ValueError("unknown component")


def reverse_ZZ_and_dequant(pic, quant_table, inp, outp):
    reverse_ZZ_u = [
        [0, 0, 1, 2, 1, 0, 0, 1],
        [2, 3, 4, 3, 2, 1, 0, 0],
        [1, 2, 3, 4, 5, 6, 5, 4],
        [3, 2, 1, 0, 0, 1, 2, 3],
        [4, 5, 6, 7, 7, 6, 5, 4],
        [3, 2, 1, 2, 3, 4, 5, 6],
        [7, 7, 6, 5, 4, 3, 4, 5],
        [6, 7, 7, 6, 5, 6, 7, 7]
    ]
    reverse_ZZ_v = [
        [0, 1, 0, 0, 1, 2, 3, 2],
        [1, 0, 0, 1, 2, 3, 4, 5],
        [4, 3, 2, 1, 0, 0, 1, 2],
        [3, 4, 5, 6, 7, 6, 5, 4],
        [3, 2, 1, 0, 1, 2, 3, 4],
        [5, 6, 7, 7, 6, 5, 4, 3],
        [2, 3, 4, 5, 6, 7, 7, 6],
        [5, 4, 5, 6, 7, 7, 6, 7]
    ]
    
    for u in range(8):
        for v in range(8):
            outp[reverse_ZZ_u[u][v]][reverse_ZZ_v[u][v]] = inp[u][v] * pic.quant_tables[quant_table][u][v]

a_c = 0.9807
b_c = 0.8314
c_c = 0.5555
d_c = 0.1950
e_c = 0.9238
f_c = 0.3826
g_c = 0.7071

tab_coefs = [
    [0.7071,  a_c,  e_c,  b_c,  g_c,  c_c,  f_c,  d_c],
    [0.7071,  b_c,  f_c, -d_c, -g_c, -a_c, -e_c, -c_c],
    [0.7071,  c_c, -f_c, -a_c, -g_c,  d_c,  e_c,  b_c],
    [0.7071,  d_c, -e_c, -c_c,  g_c,  b_c, -f_c, -a_c],
    [0.7071, -d_c, -e_c,  c_c,  g_c, -b_c, -f_c,  a_c],
    [0.7071, -c_c, -f_c,  a_c, -g_c, -d_c,  e_c, -b_c],
    [0.7071, -b_c,  f_c,  d_c, -g_c,  a_c, -e_c,  c_c],
    [0.7071, -a_c,  e_c, -b_c,  g_c, -c_c,  f_c, -d_c]
]


def data_unit_do_idct(inp, outp):
    for y in range(8):
        for x in range(8):
            sxy = 0
            for u in range(8):
                for v in range(8):
                    Svu = inp[v][u]
                    sxy += Svu * tab_coefs[x][u] * tab_coefs[y][v]
            
            sxy *= 0.25
            sxy += 128
            outp[x][y] = sxy

def print_matrix(m):
    for u in range(8):
        for v in range(8):
            print(f"{m[u][v]:5f}", end=" ")
        print()
    print()

def parse_bitmap_data(pic):
    print("parsing bitstream...")
    
    nb_bits = 0
    component = 0
    data_unit = 0
    ac_count = 0
    
    precedent_DC = [0, 0, 0, 0]
    
    nb_MCU = 0
    matrix = [[0 for _ in range(8)] for _ in range(8)]
    
    while nb_MCU < pic.nb_MCU_total:
        component = 0
        while component < pic.nb_components:
            data_unit = 0
            while data_unit < (pic.components_data[component].V * pic.components_data[component].H):
                for u in range(8):
                    for v in range(8):
                        matrix[u][v] = 0
                
                SSSS = 0
                DC = 0
                boo, SSSS, nb_bits = bitstream_get_next_decoded_element(pic, 0, pic.components_data[component].Td, SSSS, nb_bits)
                
                if not boo:
                    errx(1, "no DC data")
                if SSSS:
                    bits_DC = bitstream_get_bits(pic, SSSS)      
                    bitstream_remove_bits(pic, SSSS)
                    msb_DC = bool(bits_DC & (1 << (SSSS - 1)))
                      
                    if msb_DC:
                        DC = precedent_DC[component] + bits_DC
                    else:
                        DC = precedent_DC[component] + convert_to_neg(bits_DC, SSSS)
                else:
                    DC = precedent_DC[component] + 0
               
                matrix[0][0] = DC
                precedent_DC[component] = DC
                
                AC = 0
                ac_count = 0
                while ac_count < 63:
                    RRRRSSSS = 0
                    boo, RRRRSSSS, nb_bits = bitstream_get_next_decoded_element(pic, 1, pic.components_data[component].Ta, RRRRSSSS, nb_bits)
                    if not boo:
                        errx(1, "no AC data")

                    RRRR = RRRRSSSS >> 4
                    SSSS = RRRRSSSS & 0x0f
                    
                    if RRRR == 0 and SSSS == 0:
                        break
                    elif RRRR == 0x0F and SSSS == 0:
                        ac_count += 16
                    else:
                        ac_count += RRRR
                        
                        bits_AC = bitstream_get_bits(pic, SSSS)
                        bitstream_remove_bits(pic, SSSS)
                        
                        msb_AC = bool(bits_AC & (1 << (SSSS - 1)))
                        
                        if msb_AC:
                            AC = bits_AC
                        else:
                            AC = convert_to_neg(bits_AC, SSSS)
                        
                        u = (ac_count + 1) // 8
                        v = (ac_count + 1) % 8
                        matrix[u][v] = AC
                        ac_count += 1


                matrix_dequant = [[0 for _ in range(8)] for _ in range(8)]

                reverse_ZZ_and_dequant(pic, pic.components_data[component].Tq, matrix, matrix_dequant)
                
                matrix_decoded = [[0 for _ in range(8)] for _ in range(8)]
                data_unit_do_idct(matrix_dequant, matrix_decoded)

                store_data_unit_YCbCr(pic, nb_MCU, component, data_unit, matrix_decoded)

                data_unit += 1
            component += 1
        nb_MCU += 1
    
    print("parsed %lu MCU" % nb_MCU)

def open_new_picture(name, picture):
    with open(name, "rb") as f:
        picture.filesize = os.path.getsize(name)
        picture.data = bytearray(f.read())
    
    print(f"{picture.filesize} bytes read from {name}\n")

    picture.pos_in_file = 0
    picture.nb_components = 0
    for i in range(2):
        for j in range(2):
            picture.huff_tables[i][j].nb_entries = 0

def parse_picture(picture):
    while picture.pos_in_file < picture.filesize - 1:
        marker = get_marker(picture.data, picture.pos_in_file)
        picture.pos_in_file += 2
        
        print("marker: ", marker)

        if marker == 0xFFD8:
            print("SOI found")
        elif marker == 0xFFE1:
            skip_EXIF(picture)
        elif marker == 0xFFE0:
            parse_APP0(picture)
        elif marker == 0xFFDB:
            parse_DQT(picture)
        elif marker == 0xFFC0:
            parse_SOF0(picture)
        elif marker == 0xFFC4:
            parse_DHT(picture)
        elif marker == 0xFFDA:
            parse_SOS(picture)
            copy_bitmap_data_remove_stuffing(picture)
            parse_bitmap_data(picture)
        elif marker == 0xFFD9:
            print("EOI found")
        else:
            print(f"unknown marker 0x{marker:04x} pos {picture.pos_in_file}")
            break

        print()

def clamp(v):
    if v < 0:
        return 0
    if v > 255:
        return 255
    return int(v)


def write_ppm(pic, filename):
    print(f"writing file {filename}")
    with open(filename, "w") as out:
        out.write(f"P3\n{pic.size_X} {pic.size_Y}\n255\n")
        
        for y in range(pic.size_Y):
            for x in range(pic.size_X):
                Y = pic.pixels_YCbCr[x][y].Y
                Cb = pic.pixels_YCbCr[x][y].Cb
                Cr = pic.pixels_YCbCr[x][y].Cr
                
                r = Y + 1.402 * (Cr - 128)
                g = Y - (0.114 * 1.772 * (Cb - 128) + 0.299 * 1.402 * (Cr - 128)) / 0.587
                b = Y + 1.772 * (Cb - 128)
                out.write(f"{clamp(round(r))} {clamp(round(g))} {clamp(round(b))} ")
            out.write("\n")
    print("output file written\n")

def main():
    if len(sys.argv) != 2:
        print("Usage: python script.py <filename.jpg>")
        sys.exit(1)
    start_time = time.time()
    pic = Picture()
    open_new_picture(sys.argv[1], pic)
    parse_picture(pic)
    end_time = time.time()
    write_ppm(pic, "decodedimage.ppm")
    write_time = time.time()
    elapsed_time_algo = end_time - start_time
    elapsed_time_write = write_time - end_time
    
    print(f"Time taken by the Jpeg decoder algorithm: {elapsed_time_algo} seconds")
    print(f"Time taken for writing the image: {elapsed_time_write} seconds")

if __name__ == "__main__":
    main()


--- README.md ---

# Mojo Introduction
Mojo is a super-charged language that delivers C-like performance with the user-friendliness of Python. It's designed to bridge the gap between research and production, empowering you to write lightning-fast code without sacrificing readability or ease of use. Think of it as Python on steroids, ready to tackle computationally intensive tasks with ease. Mojo is a young but ambitious programming language making waves in the AI world. Mojo aims to address a key pain point: unlocking AI hardware's full potential without sacrificing developer experience. Currently in a preview release, Mojo has already garnered interest from both academia and industry. While not yet open-source, the creators plan to make it so in the future
# JPEG Decoder Overview
A JPEG decoder is a crucial component that interprets and decodes JPEG image files, facilitating their display or manipulation. It processes the encoded data, performing tasks such as entropy decoding, color space conversion, and dequantization to reconstruct the image.
# Implementation in Three Languages
I implemented the JPEG decoder in three programming languages: C, Python, and Mojo. The initial implementation, already available to me, was in C and served as the baseline:<br>
https://github.com/kittennbfive/kittenJPEG/tree/main <br> 
While I optimized the C implementation by removing unnecessary computations, the Python and Mojo versions were developed from scratch. I translated the new C code into both Python and Mojo, enabling a comprehensive comparison of their performance and efficiency. For each implementation, three images of different sizes were considered to measure the performance: (480, 680, 3), (848, 875, 3), and (2827, 2920, 3) where these dimensions represent the RGB pixel sizes of the images under consideration
# Specifications
All of the three codes will work only for Baseline JPEG with Huffman-Encoding, 8 bit precision and YCbCr-data. EXIF-data will be skipped without being decoded, just use something like ```exiftool```. This code does support Chroma-subsampling and dimensions that are not a multiple of 8 pixels. If you just want to view a jpg-file this is not what you are looking for at all, there are tons of image-viewers for all existing operating systems. However if you want to know more about the internals of JPEG, write or debug your own decoder this code could be handy. The main purpose of this is to see the performance difference when the same algorithm is implemented between 3 different languages.
# Language Comparison
In the performance evaluation, C demonstrated the highest execution speed, followed by Mojo, which exhibited approximately 1.5 times lower performance than C. Python, being an interpreted language, showed the slowest execution speed, lagging nearly 100 times behind C. The y-axis in the line graph is presented in logarithmic cycles in millions to enhance the visibility of differences between Mojo, C, and Python implementations. The x-axis shows categories of different sized images. The line graph visualizes the performance across the three image sizes, providing a comprehensive comparison.
![alt text](https://github.com/taalhaataahir0102/Jpeg-Decoder/blob/main/graph/graph.png)

The following table shows the total cycles taken by the Jpeg decoder algorithm (excluding the cycles taken for file reading and writing) for each language:

![alt text](https://github.com/taalhaataahir0102/Jpeg-Decoder/blob/main/graph/table.png)

These cycles were measured through perf profiling tool



--- LICENSE ---

MIT License

Copyright (c) 2024 Maxim Zaks

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


--- README.md ---

`compact-dict` is a fast hashmap based dictionary implemented in Mojo 🔥.

Although the dictionary is fast (currently it is about 10x faster than the std `Dict`) its main concern is with reducing memory footprint.

We introduce two self sufficient modules:
- `string_dict` where the key type of the dictionary is a `String`
- `generic_dict` which allows keys to be of any type conforming with `Keyable` trait

Both modules expose a `Dict` struct which has the following compile time parametrization options:
- Value type can be any type conforming with `CollectionElement` trait
- We use a fast hash function as default, but you can provide your own hash function
- By setting the `KeyCountType` to a lower unsigned DType e.g. (`DType.uint8` or `DType.uint16`) we can reduce the memory footprint. The type needs to be able to represent number of keys
- By setting the `KeyOffsetType` to a lower unsigned DType we can reduce the memory footprint even further. The type needs to be able to represent the sum of all key bytes
- Set `destructive` to `False` if you don't intend to delete keys from the dict. This way we do not waste space for deleted flags
- Set `caching_hashes` to `False` in order to reduce memory footprint by not caching the hash values. Keep in mind that this change slows down the rehashing process

The `Dict` can be instantiated with a `capacity` value. Default is set to 16, min capacity is 8. If you know the number of elements ahead of time set it, this will avoid rehashing and might improve memory footprint.

### Sample code for generic dict:
```
from generic_dict import Dict, Keyable, KeysBuilder
from testing import assert_equal

@value
struct Person(Keyable):
    var name: String
    var age: Int

    fn accept[T: KeysBuilder](self, inout keys_builder: T):
        keys_builder.add_buffer[DType.int8](self.name._as_ptr(), len(self.name))
        keys_builder.add(Int64(self.age))

fn test_person_dict() raises:
    let p1 = Person("Maxim", 42)
    let p2 = Person("Maximilian", 62)
    let p3 = Person("Alex", 25)
    let p4 = Person("Maria", 28)
    let p5 = Person("Daria", 13)
    let p6 = Person("Max", 31)

    var d = Dict[Int]()
    d.put(p1, 1)
    d.put(p2, 11)
    d.put(p3, 111)
    d.put(p4, 1111)
    d.put(p5, 11111)
    d.put(p6, 111111)

    assert_equal(d.get(p1, 0), 1)
    assert_equal(d.get(p2, 0), 11)
    assert_equal(d.get(p3, 0), 111)
    assert_equal(d.get(p4, 0), 1111)
    assert_equal(d.get(p5, 0), 11111)
    assert_equal(d.get(p6, 0), 111111)

```

### Note:
Due to a bug in Mojo 24.1 `generic_dict` module does not compile
Bug report https://github.com/modularml/mojo/issues/1858 

--- benchmark_generic_dict.mojo ---

import benchmark
from generic_dict import Dict, Keyable, KeysBuilder
from collections.dict import KeyElement, Dict as StdDict
from pathlib import cwd
from testing import assert_equal

from corpora import *


@value
struct StringKey(KeyElement, Keyable):
    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:
        var ptr = self.s.unsafe_ptr()
        return hash(ptr, len(self.s))

    fn __eq__(self, other: Self) -> Bool:
        return self.s == other.s

    fn __ne__(self, other: Self) -> Bool:
        return self.s != other.s

    fn accept[T: KeysBuilder](self, inout keys_builder: T):
        keys_builder.add_buffer(self.s.unsafe_ptr(), len(self.s))

fn corpus_stats(corpus: List[String]):
    print("=======Corpus Stats=======")
    print("Number of elements:", len(corpus))
    var min = 100000000
    var max = 0
    var sum = 0
    var count = 0
    for i in range(len(corpus)):
        var key = corpus[i]
        if len(key) == 0:
            continue
        count += 1
        sum += len(key)
        if min > len(key):
            min = len(key)
        if max < len(key):
            max = len(key)
    var avg = sum / count
    print("Min key lenght:", min)
    print("Avg key length:", avg)
    print("Max key length:", max)
    print("Total num of bytes:", sum)
    print("\n")

fn main() raises:
    var d1 = Dict[Int]()
    var d2 = StdDict[StringKey, Int]()
    var corpus = french_text_to_keys()
    
    print("")
    corpus_stats(corpus)

    @parameter
    fn build_compact_dict():
        var d = Dict[Int](len(corpus))
        # var d = Dict[Int]()
        for i in range(len(corpus)):
            try:
                _ = d.put(StringKey(corpus[i]), i)
            except:
                print("!!!")
        d1 = d^

    @parameter
    fn build_std_dict():
        var d = StdDict[StringKey, Int]()
        for i in range(len(corpus)):
            d[corpus[i]] = i
        d2 = d^

    print("+++++++Create Dict Benchmark+++++++")

    var build_compact_stats = benchmark.run[build_compact_dict](max_runtime_secs=0.5)
    # build_compact_stats.print("ns")

    var build_std_stats = benchmark.run[build_std_dict](max_runtime_secs=0.5)
    # build_std_stats.print("ns")

    print("Compact build speedup:", build_std_stats.mean() / build_compact_stats.mean())
    var sum1 = 0
    @parameter
    fn read_compact_dict():
        sum1 = 0
        for i in range(len(corpus)):
            try:
                sum1 += d1.get(StringKey(corpus[i]), -1)
            except:
                print("!!!!!")

    # d1.keys.print_keys()
    print("+++++++Read Dict Benchmark+++++++")
    var read_compact_stats = benchmark.run[read_compact_dict](max_runtime_secs=0.5)
    print("Sum1:", sum1, len(d1))
    # read_compact_stats.print("ns")

    var sum2 = 0
    @parameter
    fn read_std_dict():
        sum2 = 0
        for i in range(len(corpus)):
            try:
                sum2 += d2[corpus[i]]
            except:
                sum2 += -1

    var raed_std_stats = benchmark.run[read_std_dict](max_runtime_secs=0.5)
    # raed_std_stats.print("ns")
    print("Sum2:", sum2, len(d2))
    print("Compact read speedup:", raed_std_stats.mean() / read_compact_stats.mean())
    
    assert_equal(sum1, sum2)
    assert_equal(len(d1), len(d2))

    var m = 9
    @parameter
    fn delete_compact_dict():
        for i in range(len(corpus)):
            if i % m == 0:
                try:
                    d1.delete(StringKey(corpus[i]))
                except:
                    print("!!!!!!!!!!!!!!")

    @parameter
    fn delete_std_dict():
        for i in range(len(corpus)):
            if i % m == 0:
                try:
                    _ = d2.pop(corpus[i])
                except:
                    pass

    print("+++++++Delete Dict Benchmark+++++++")

    var delete_compact_stats = benchmark.run[delete_compact_dict](max_runtime_secs=0.5)
    var delete_std_stats = benchmark.run[delete_std_dict](max_runtime_secs=0.5)

    print("Compact delete speedup:", delete_std_stats.mean() / delete_compact_stats.mean())

    print("+++++++Read After Delete Dict Benchmark+++++++")

    var read_after_delete_compact_stats = benchmark.run[read_compact_dict](max_runtime_secs=0.5)
    var read_after_delete_std_stats = benchmark.run[read_std_dict](max_runtime_secs=0.5)

    print("Compact read after delete speedup:", read_after_delete_std_stats.mean() / read_after_delete_compact_stats.mean())

    print("Sum1:", sum1, "length:", len(d1))
    print("Sum2:", sum2, "length:", len(d2))

    assert_equal(sum1, sum2)
    assert_equal(len(d1), len(d2))

    _ = corpus
    _ = d1^
    _ = d2^

--- benchmark_multi_dict.mojo ---

import benchmark
from generic_dict import MultiDict, Keyable, KeysBuilder
from collections.dict import KeyElement, Dict as StdDict
from pathlib import cwd
from testing import assert_equal

from corpora import *


@value
struct StringKey(KeyElement, Keyable):
    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:
        var ptr = self.s.unsafe_ptr()
        return hash(ptr, len(self.s))

    fn __eq__(self, other: Self) -> Bool:
        return self.s == other.s

    fn __ne__(self, other: Self) -> Bool:
        return self.s != other.s

    fn accept[T: KeysBuilder](self, inout keys_builder: T):
        keys_builder.add_buffer(self.s.unsafe_ptr(), len(self.s))

fn corpus_stats(corpus: List[String]):
    print("=======Corpus Stats=======")
    print("Number of elements:", len(corpus))
    var min = 100000000
    var max = 0
    var sum = 0
    var count = 0
    for i in range(len(corpus)):
        var key = corpus[i]
        if len(key) == 0:
            continue
        count += 1
        sum += len(key)
        if min > len(key):
            min = len(key)
        if max < len(key):
            max = len(key)
    var avg = sum / count
    print("Min key lenght:", min)
    print("Avg key length:", avg)
    print("Max key length:", max)
    print("Total num of bytes:", sum)
    print("\n")

fn main() raises:
    var d1 = MultiDict[Int]()
    var d2 = StdDict[StringKey, Int]()
    var corpus = french_text_to_keys()
    
    print("")
    corpus_stats(corpus)

    @parameter
    fn build_compact_dict():
        var d = MultiDict[Int](len(corpus))
        # var d = MultiDict[Int]()
        for i in range(len(corpus)):
            try:
                d.put(StringKey(corpus[i]), i)
            except:
                print("!!!")
        d1 = d^

    @parameter
    fn build_std_dict():
        var d = StdDict[StringKey, Int]()
        for i in range(len(corpus)):
            d[corpus[i]] = i
        d2 = d^

    print("+++++++Create Dict Benchmark+++++++")

    var build_compact_stats = benchmark.run[build_compact_dict](max_runtime_secs=0.5)
    # build_compact_stats.print("ns")

    var build_std_stats = benchmark.run[build_std_dict](max_runtime_secs=0.5)
    # build_std_stats.print("ns")

    print("Compact build speedup:", build_std_stats.mean() / build_compact_stats.mean())
    var sum1 = 0
    @parameter
    fn read_compact_dict():
        sum1 = 0
        for i in range(len(corpus)):
            try:
                var v = d1.get(StringKey(corpus[i]))
                sum1 += v[len(v) - 1]
            except:
                print("!!!!!")

    # d1.keys.print_keys()
    print("+++++++Read Dict Benchmark+++++++")
    var read_compact_stats = benchmark.run[read_compact_dict](max_runtime_secs=0.5)
    print("Sum1:", sum1, len(d1))
    # read_compact_stats.print("ns")

    var sum2 = 0
    @parameter
    fn read_std_dict():
        sum2 = 0
        for i in range(len(corpus)):
            try:
                sum2 += d2[corpus[i]]
            except:
                sum2 += -1

    var raed_std_stats = benchmark.run[read_std_dict](max_runtime_secs=0.5)
    # raed_std_stats.print("ns")
    print("Sum2:", sum2, len(d2))
    print("Compact read speedup:", raed_std_stats.mean() / read_compact_stats.mean())
    
    assert_equal(sum1, sum2)
    assert_equal(len(d1), len(d2))

    _ = corpus
    _ = d1^
    _ = d2^

--- benchmark_report_string_dict.mojo ---

import benchmark
from string_dict import Dict as CompactDict
from collections.dict import KeyElement, Dict as StdDict
from pathlib import cwd
from testing import assert_equal
from csv import CsvBuilder

from corpora import *

alias M = 9

@value
struct BenchmarkData:
    var reports: List[benchmark.Report]
    var read_checksums: List[Int]

    fn __init__(inout self):
        self.reports = List[benchmark.Report]()
        self.read_checksums = List[Int]()

fn report_std_benchmarks(corpus: List[String], inout csv_builder: CsvBuilder) -> BenchmarkData:
    var benchmark_data = BenchmarkData()
    var std_dict = StdDict[String, Int]()
    @parameter
    fn build_dict():
        var d = StdDict[String, Int]()
        for i in range(len(corpus)):
            d[corpus[i]] = i
        std_dict = d^
    var build_stats = benchmark.run[build_dict](max_runtime_secs=0.5)
    csv_builder.push(str(build_stats.mean("ns")), False)
    benchmark_data.reports.append(build_stats)

    var sum = 0
    @parameter
    fn read_dict():
        sum = 0
        for i in range(len(corpus)):
            try:
                sum += std_dict[corpus[i]]
            except:
                sum += -1

    var read_stats = benchmark.run[read_dict](max_runtime_secs=0.5)
    csv_builder.push(str(read_stats.mean("ns")), False)
    benchmark_data.reports.append(read_stats)
    benchmark_data.read_checksums.append(sum)

    @parameter
    fn delete_dict():
        for i in range(len(corpus)):
            if i % M == 0:
                try:
                    _ = std_dict.pop(corpus[i])
                except:
                    pass
    
    var delete_stats = benchmark.run[delete_dict](max_runtime_secs=0.5)
    csv_builder.push(str(delete_stats.mean("ns")), False)
    benchmark_data.reports.append(delete_stats)

    var read_after_delete_stats = benchmark.run[read_dict](max_runtime_secs=0.5)
    csv_builder.push(str(read_after_delete_stats.mean("ns")), False)
    benchmark_data.reports.append(read_after_delete_stats)
    benchmark_data.read_checksums.append(sum)

    _ = std_dict

    return benchmark_data


fn report_compact_benchmarks(corpus: List[String], inout csv_builder: CsvBuilder) -> BenchmarkData:
    var benchmark_data = BenchmarkData()
    var dict = CompactDict[Int]()
    @parameter
    fn build_dict_nc():
        var d = CompactDict[Int]()
        for i in range(len(corpus)):
            d.put(corpus[i], i)
        dict = d^
    var build_stats_nc = benchmark.run[build_dict_nc](max_runtime_secs=0.5)
    csv_builder.push(str(build_stats_nc.mean("ns")), False)
    benchmark_data.reports.append(build_stats_nc)

    @parameter
    fn build_dict():
        var d = CompactDict[Int](len(corpus))
        for i in range(len(corpus)):
            d.put(corpus[i], i)
        dict = d^
    var build_stats = benchmark.run[build_dict](max_runtime_secs=0.5)
    csv_builder.push(str(build_stats.mean("ns")), False)
    benchmark_data.reports.append(build_stats)

    var sum = 0
    @parameter
    fn read_dict():
        sum = 0
        for i in range(len(corpus)):
            sum += dict.get(corpus[i], -1)

    var read_stats = benchmark.run[read_dict](max_runtime_secs=0.5)
    var read_checksum = sum
    csv_builder.push(str(read_stats.mean("ns")), False)
    benchmark_data.reports.append(read_stats)
    benchmark_data.read_checksums.append(sum)

    @parameter
    fn delete_dict():
        for i in range(len(corpus)):
            if i % M == 0:
                dict.delete(corpus[i])
    
    var delete_stats = benchmark.run[delete_dict](max_runtime_secs=0.5)
    csv_builder.push(str(delete_stats.mean("ns")), False)
    benchmark_data.reports.append(delete_stats)

    var read_after_delete_stats = benchmark.run[read_dict](max_runtime_secs=0.5)
    var read_after_delete_checksum = sum

    csv_builder.push(str(read_after_delete_stats.mean("ns")), False)
    benchmark_data.reports.append(read_after_delete_stats)
    benchmark_data.read_checksums.append(sum)
    _ = dict
    return benchmark_data

fn corpus_stats(corpus: List[String], inout csv_builder: CsvBuilder):
    csv_builder.push(str(len(corpus)), False)
    var min = 100000000
    var max = 0
    var sum = 0
    var count = 0
    for i in range(len(corpus)):
        var key = corpus[i]
        if len(key) == 0:
            continue
        count += 1
        sum += len(key)
        if min > len(key):
            min = len(key)
        if max < len(key):
            max = len(key)
    var avg = sum / count
    csv_builder.push(str(sum), False)
    csv_builder.push(str(min), False)
    csv_builder.push(str(avg), False)
    csv_builder.push(str(max), False)

fn report_speedup(std: BenchmarkData, compact: BenchmarkData, inout csv_builder: CsvBuilder):
    csv_builder.push(str(std.reports[0].mean() / compact.reports[0].mean()), False)
    csv_builder.push(str(std.reports[0].mean() / compact.reports[1].mean()), False)
    csv_builder.push(str(std.reports[1].mean() / compact.reports[2].mean()), False)
    csv_builder.push(str(std.reports[2].mean() / compact.reports[3].mean()), False)
    csv_builder.push(str(std.reports[3].mean() / compact.reports[4].mean()), False)

fn report_checksums_alignment(std: BenchmarkData, compact: BenchmarkData, inout csv_builder: CsvBuilder):
    csv_builder.push(str(std.read_checksums[0] == compact.read_checksums[0]), False)
    csv_builder.push(str(std.read_checksums[1] == compact.read_checksums[1]), False)

fn report(name: StringLiteral, corpus: List[String], inout csv_builder: CsvBuilder):
    csv_builder.push(name, False)
    corpus_stats(corpus, csv_builder)
    var std_stats = report_std_benchmarks(corpus, csv_builder)
    var compact_stats = report_compact_benchmarks(corpus, csv_builder)
    report_speedup(std_stats, compact_stats, csv_builder)
    report_checksums_alignment(std_stats, compact_stats, csv_builder)

fn main() raises:
    # Crashes because of this bug https://github.com/modularml/mojo/issues/2829

    var csv_builder = CsvBuilder(
        "Corpus", "Number of keys", "Total bytes", "Min key", "Avg key", "Max key", 
        "Build stdlib", "Read stdlib", "Delete stdlib", "Read after delete stdlib",
        "Build compact nc", "Build compact", "Read compact", "Delete compact", "Read after delete compact",
        "Speedup build nc", "Speedup build", "Speedup read", "Speadup delete", "Speedup read after delete",
        "Read Checksum", "Read Checksum after delete"
    )
    report("Arabic", arabic_text_to_keys(), csv_builder)
    report("Chinese", chinese_text_to_keys(), csv_builder)
    report("English", english_text_to_keys(), csv_builder)
    report("French", french_text_to_keys(), csv_builder)
    report("Georgien", georgian_text_to_keys(), csv_builder)
    report("German", german_text_to_keys(), csv_builder)
    report("Greek", greek_text_to_keys(), csv_builder)
    report("Hebrew", hebrew_text_to_keys(), csv_builder)
    report("Hindi", hindi_text_to_keys(), csv_builder)
    report("Japanese", japanese_long_keys(), csv_builder)
    report("l33t", l33t_text_to_keys(), csv_builder)
    report("Russian", russian_text_to_keys(), csv_builder)
    report("S3", s3_action_names(), csv_builder)
    report("Words", system_words_collection(), csv_builder)
    print(csv_builder^.finish())


--- benchmark_string_dict.mojo ---

import benchmark
from string_dict import Dict as CompactDict
from collections.dict import KeyElement, Dict as StdDict
from pathlib import cwd
from testing import assert_equal

from corpora import *


fn corpus_stats(corpus: List[String]):
    print("=======Corpus Stats=======")
    print("Number of elements:", len(corpus))
    var min = 100000000
    var max = 0
    var sum = 0
    var count = 0
    for i in range(len(corpus)):
        var key = corpus[i]
        if len(key) == 0:
            continue
        count += 1
        sum += len(key)
        if min > len(key):
            min = len(key)
        if max < len(key):
            max = len(key)
    var avg = sum / count
    print("Min key lenght:", min)
    print("Avg key length:", avg)
    print("Max key length:", max)
    print("Total num of bytes:", sum)
    print("\n")

fn main() raises:
    var d1 = CompactDict[Int]()
    var d2 = StdDict[String, Int]()
    var corpus = french_text_to_keys()
    
    print("")
    corpus_stats(corpus)

    @parameter
    fn build_compact_dict():
        var d = CompactDict[Int](len(corpus))
        # var d = CompactDict[Int]()
        for i in range(len(corpus)):
            d.put(corpus[i], i)
        d1 = d^

    @parameter
    fn build_std_dict():
        var d = StdDict[String, Int]()
        for i in range(len(corpus)):
            d[corpus[i]] = i
        d2 = d^

    print("+++++++Create Dict Benchmark+++++++")

    var build_compact_stats = benchmark.run[build_compact_dict](max_runtime_secs=0.5)
    # build_compact_stats.print("ns")

    var build_std_stats = benchmark.run[build_std_dict](max_runtime_secs=0.5)
    # build_std_stats.print("ns")

    print("Compact build speedup:", build_std_stats.mean() / build_compact_stats.mean())
    var sum1 = 0
    @parameter
    fn read_compact_dict():
        sum1 = 0
        for i in range(len(corpus)):
            sum1 += d1.get(corpus[i], -1)

    # d1.keys.print_keys()
    print("+++++++Read Dict Benchmark+++++++")
    var read_compact_stats = benchmark.run[read_compact_dict](max_runtime_secs=0.5)
    print("Sum1:", sum1, len(d1))
    # read_compact_stats.print("ns")

    var sum2 = 0
    @parameter
    fn read_std_dict():
        sum2 = 0
        for i in range(len(corpus)):
            try:
                sum2 += d2[corpus[i]]
            except:
                sum2 += -1

    var raed_std_stats = benchmark.run[read_std_dict](max_runtime_secs=0.5)
    # raed_std_stats.print("ns")
    print("Sum2:", sum2, len(d2))
    print("Compact read speedup:", raed_std_stats.mean() / read_compact_stats.mean())
    
    assert_equal(sum1, sum2)
    assert_equal(len(d1), len(d2))

    var m = 9
    @parameter
    fn delete_compact_dict():
        for i in range(len(corpus)):
            if i % m == 0:
                d1.delete(corpus[i])

    @parameter
    fn delete_std_dict():
        for i in range(len(corpus)):
            if i % m == 0:
                try:
                    _ = d2.pop(corpus[i])
                except:
                    pass

    print("+++++++Delete Dict Benchmark+++++++")

    var delete_compact_stats = benchmark.run[delete_compact_dict](max_runtime_secs=0.5)
    var delete_std_stats = benchmark.run[delete_std_dict](max_runtime_secs=0.5)

    print("Compact delete speedup:", delete_std_stats.mean() / delete_compact_stats.mean())

    print("+++++++Read After Delete Dict Benchmark+++++++")

    var read_after_delete_compact_stats = benchmark.run[read_compact_dict](max_runtime_secs=0.5)
    var read_after_delete_std_stats = benchmark.run[read_std_dict](max_runtime_secs=0.5)

    print("Compact read after delete speedup:", read_after_delete_std_stats.mean() / read_after_delete_compact_stats.mean())

    print("Sum1:", sum1, "length:", len(d1))
    print("Sum2:", sum2, "length:", len(d2))

    assert_equal(sum1, sum2)
    assert_equal(len(d1), len(d2))

    _ = corpus
    _ = d1^
    _ = d2^

--- checkout_remote_modules.sh ---

#!/bin/bash

function check_out_remote_module() (
    rurl="$1"
    shift
    declare -a paths
    declare -a module_names
    for var in "$@" 
    do
        IFS="="
        read -ra module_name_components <<< "$var"
        components_count=${#module_name_components[@]}
        path=${module_name_components[0]}
        module_name=${module_name_components[$components_count-1]}
        paths=("${paths[@]}" "$path")
        module_names=("${module_names[@]}" "$module_name")
    done
    IFS=" "

    for module_name in "${module_names[@]}" 
    do
        rm -rf ../$module_name
    done
    
    current_date_time=$(date)
    echo "URL: $rurl"
    git clone -n --depth=1 --filter=tree:0 $rurl
    cd ${rurl##*/}
    git sparse-checkout set --no-cone "${paths[@]}"
    git checkout
    
    for i in "${!paths[@]}" 
    do
        module_name=${module_names[$i]}
        path=${paths[$i]}
        cp -R ./$path ../../$module_name
        echo $current_date_time > ../../$module_name/.checkoutinfo
        echo "URL: $rurl" >> ../../$module_name/.checkoutinfo
        echo "Path: $path" >> ../../$module_name/.checkoutinfo
    done
    cd ../
)

function checkout()(
    # Add check out remote module calls here
    
    check_out_remote_module "https://github.com/mzaks/mojo-csv" "csv"
)

mkdir -p "_deps"
cd "_deps"

checkout

rm -rf "../_deps"

--- corpora/__init__.mojo ---

from pathlib import cwd, Path

fn english_text_to_keys() raises -> List[String]:
    return String('A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions! A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions! A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now. When, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath of that universal love which bears and sustains us, as it floats around us in an eternity of bliss; and then, my friend, when darkness overspreads my eyes, and heaven and earth seem to dwell in my soul and absorb its power, like the form of a beloved mistress, then I often think with longing, Oh, would I could describe these conceptions, could impress upon paper all that is living so full and warm within me, that it might be the mirror of my soul, as my soul is the mirror of the infinite God! O my friend -- but it is too much for my strength -- I sink under the weight of the splendour of these visions!A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls').split(" ")

fn greek_text_to_keys() raises -> List[String]:
    return (cwd() / "corpora" / "greek.txt").read_text().replace("\n", " ").split(" ")

fn hebrew_text_to_keys() raises -> List[String]:
    return (cwd() / "corpora" / "hebrew.txt").read_text().replace("\n", " ").split(" ")

fn arabic_text_to_keys() raises -> List[String]:
    return (cwd() / "corpora" / "arabic.txt").read_text().replace("\n", " ").split(" ")

fn l33t_text_to_keys() raises -> List[String]:
    return (cwd() / "corpora" / "l33t.txt").read_text().replace("\n", " ").split(" ")

fn georgian_text_to_keys() raises -> List[String]:
    return (cwd() / "corpora" / "georgian.txt").read_text().replace("\n", " ").split(" ")

fn chinese_text_to_keys() raises -> List[String]:
    return (cwd() / "corpora" / "chinese.txt").read_text().replace("\n", " ").split(" ")

fn french_text_to_keys() raises -> List[String]:
    return (cwd() / "corpora" / "french.txt").read_text().replace("\n", " ").split(" ")

fn hindi_text_to_keys() raises -> List[String]:
    return (cwd() / "corpora" / "hindi.txt").read_text().replace("\n", " ").split(" ")

fn russian_text_to_keys() raises -> List[String]:
    return String('Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое. Лежа на панцирнотвердой спине, он видел, стоило ему приподнять голову, свой коричневый, выпуклый, разделенный дугообразными чешуйками живот, на верхушке которого еле держалось готовое вот-вот окончательно сползти одеяло. Его многочисленные, убого тонкие по сравнению с остальным телом ножки беспомощно копошились у него перед глазами. «Что со мной случилось?» – подумал он. Это не было сном. Его комната, настоящая, разве что слишком маленькая, но обычная комната, мирно покоилась в своих четырех хорошо знакомых стенах. Над столом, где были разложены распакованные образцы сукон – Замза был коммивояжером, – висел портрет, который он недавно вырезал из иллюстрированного журнала и вставил в красивую золоченую рамку. На портрете была изображена дама в меховой шляпе и боа, она сидела очень прямо и протягивала зрителю тяжелую меховую муфту, в которой целиком исчезала ее рука. Затем взгляд Грегора устремился в окно, и пасмурная погода – слышно было, как по жести подоконника стучат капли дождя – привела его и вовсе в грустное настроение. «Хорошо бы еще немного поспать и забыть всю эту чепуху», – подумал он, но это было совершенно неосуществимо, он привык спать на правом боку, а в теперешнем своем состоянии он никак не мог принять этого положения. С какой бы силой ни поворачивался он на правый бок, он неизменно сваливался опять на спину. Закрыв глаза, чтобы не видеть своих барахтающихся ног, он проделал это добрую сотню раз и отказался от этих попыток только тогда, когда почувствовал какую-то неведомую дотоле, тупую и слабую боль в боку. «Ах ты, господи, – подумал он, – какую я выбрал хлопотную профессию! Изо дня в день в разъездах. Деловых волнений куда больше, чем на месте, в торговом доме, а кроме того, изволь терпеть тяготы дороги, думай о расписании поездов, мирись с плохим, нерегулярным питанием, завязывай со все новыми и новыми людьми недолгие, никогда не бывающие сердечными отношения. Черт бы побрал все это!» Он почувствовал вверху живота легкий зуд; медленно подвинулся на спине к прутьям кровати, чтобы удобнее было поднять голову; нашел зудевшее место, сплошь покрытое, как оказалось, белыми непонятными точечками; хотел было ощупать это место одной из ножек, но сразу отдернул ее, ибо даже простое прикосновение вызвало у него, Грегора, озноб. Он соскользнул в прежнее свое положение. «От этого раннего вставания, – подумал он, – можно совсем обезуметь. Человек должен высыпаться. Другие коммивояжеры живут, как одалиски. Когда я, например, среди дня возвращаюсь в гостиницу, чтобы переписать полученные заказы, эти господа только завтракают. А осмелься я вести себя так, мои хозяин выгнал бы меня сразу. Кто знает, впрочем, может быть, это было бы даже очень хорошо для меня. Если бы я не сдерживался ради родителей, я бы давно заявил об уходе, я бы подошел к своему хозяину и выложил ему все, что о нем думаю. Он бы так и свалился с конторки! Странная у него манера – садиться на конторку и с ее высоты разговаривать со служащим, который вдобавок вынужден подойти вплотную к конторке из-за того, что хозяин туг на ухо. Однако надежда еще не совсем потеряна: как только я накоплю денег, чтобы выплатить долг моих родителей – на это уйдет еще лет пять-шесть, – я так и поступлю. Тут-то мы и распрощаемся раз и навсегда. А пока что надо подниматься, мой поезд отходит в пять». И он взглянул на будильник, который тикал на сундуке. «Боже правый!» – подумал он. Было половина седьмого, и стрелки спокойно двигались дальше, было даже больше половины, без малого уже три четверти. Неужели будильник не звонил? С кровати было видно, что он поставлен правильно, на четыре часа; и он, несомненно, звонил. Но как можно было спокойно спать под этот сотрясающий мебель трезвон? Ну, спал-то он неспокойно, но, видимо, крепко. Однако что делать теперь? Следующий поезд уходит в семь часов; чтобы поспеть на него, он должен отчаянно торопиться, а набор образцов еще не упакован, да и сам он отнюдь не чувствует себя свежим и легким на подъем. И даже поспей он на поезд, хозяйского разноса ему все равно не избежать – ведь рассыльный торгового дома дежурил у пятичасового поезда и давно доложил о его, Грегора, опоздании. Рассыльный, человек бесхарактерный и неумный, был ставленником хозяина. А что, если сказаться больным? Но это было бы крайне неприятно и показалось бы подозрительным, ибо за пятилетнюю свою службу Грегор ни разу еще не болел. Хозяин, конечно, привел бы врача больничной кассы и стал попрекать родителей сыном-лентяем, отводя любые возражения ссылкой на этого врача, по мнению которого все люди на свете совершенно здоровы и только не любят работать. И разве в данном случае он был бы так уж неправ? Если не считать сонливости, действительно странной после такого долгого сна, Грегор и в самом деле чувствовал себя превосходно и был даже чертовски голоден.Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое. Лежа на панцирнотвердой спине, он видел, стоило ему приподнять голову, свой коричневый, выпуклый, разделенный дугообразными чешуйками живот, на верхушке которого еле держалось готовое вот-вот окончательно сползти одеяло. Его многочисленные, убого тонкие по сравнению с остальным телом ножки беспомощно копошились у него перед глазами. «Что со мной случилось?» – подумал он. Это не было сном. Его комната, настоящая, разве что слишком маленькая, но обычная комната, мирно покоилась в своих четырех хорошо знакомых стенах. Над столом, где были разложены распакованные образцы сукон – Замза был коммивояжером, – висел портрет, который он недавно вырезал из иллюстрированного журнала и вставил в красивую золоченую рамку. На портрете была изображена дама в меховой шляпе и боа, она сидела очень прямо и протягивала зрителю тяжелую меховую муфту, в которой целиком исчезала ее рука. Затем взгляд Грегора устремился в окно, и пасмурная погода – слышно было, как по жести подоконника стучат капли дождя – привела его и вовсе в грустное настроение. «Хорошо бы еще немного поспать и забыть всю эту чепуху», – подумал он, но это было совершенно неосуществимо, он привык спать на правом боку, а в теперешнем своем состоянии он никак не мог принять этого положения. С какой бы силой ни поворачивался он на правый бок, он неизменно сваливался опять на спину.').split(" ")

fn german_text_to_keys() raises -> List[String]:
    return String('Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort "und" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch. Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort "und" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch. Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rhetorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort "und" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch.Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht – ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen').split(" ")

fn japanese_long_keys() raises -> List[String]:
    return String('米くを舵4物委らご氏松ハナテフ月関ソ時平ふいの博情れじフ牟万い元56園フメヤオ試図ロツヤ未備王こと傷喫羅踊んゆし。栃ユヱオ書著作ユソツロ英祉業ア大課ご権質フべ空8午キ切軟づン著郎そゃす格町採ヱオマコ処8付国ムハチア究表でなだ際無ロミヱ地兵ぴげ庭体すク発抜爆位や。楽富むゆず盛航カナセ携代ハ本高きた員59今骸ンラえぜ城解イケ穴訴ぽぎ属住ヤケトヌ抱点ト広注厚でて。 国リ出難セユメ軍手ヘカウ画形サヲシ猛85用ヲキミ心死よしと身処ケヨミオ教主ーぽ事業んく字国たさょ図能シミスヤ社8板ル岡世58次戒知院んれり。市メ誘根カ数問禁竹ゃれえみ給辺のでみき今二ぎさ裕止過こクすと無32郎所ラた生展ヌヘス成度慣葬勇厘ばてか。室ゃ下携疲ム色権がぽりっ銃週ノオ姫千テム健蔵い研手ッ放容ル告属め旅側26企サノヨ宅都福ぞ通待ちぴね種脳イど労希望義通むン。 罰しい続負せ著低たル異師ユハワ東添質コチ転集ルヤ雇聴約ヒ前統らた情厳ゆさでや真胸や有披暑棚豆ゆぼたけ。盛ワセロナ情競クるっわ講3音ずをせ少地めしぜょ手63明視れに判企ヒヌエソ求総58特本ね井比ユラキ禁頭馬るゅリす能率率かがさわ。葉サソ医郡ヱヘソ労帰ナケスミ救写ワヘ株審ネヒニミ安逮イ人画ラ涯車はラ極騒りなド件5級ンかふー劇41著ぱぐ凱討だ文世ぶづどま界善魅マ渓経競融れがや。 連ーぜらご模分ッ視外ばフく運発群ほぼづ育越一ほごクけ案募ヲイソ治会イせフ製君ぜた漢村1変リヒ構5際ツ御文ヲ臭入さドぼ代書ハケ引技ろみれ回観注倉徹ぱ。論ラづ海要サ情座ゃり齢宣ラモエ芸化エマホ覧催回ら戦69本外ト葬岳な政画か連針ぴリフず。約ル闘辺ぽ経2応掲ホサアラ塾小コラ画決クノオ上室レヌヱ勝逮ぜるえむ責豊チノ明意ひけ訟6碁草メタチエ財午召喝塊む。 決めでわ名金つけレわ続人県約ぽぼす尾腹ユサ戦載リシ護賀レモフツ重涯ニ治者むんっみ職更カタチレ提話2何ワ責東まけげふ能政ヌ供禁がびてわ提改倶れめ。読み担後ぽ安加ぎ論鹿ツ統最お気麻月つじもあ竜思いろめ判必満理トコ文連ムイウハ寄串ざほびー。文ゆこっ向27年メイ便能ノセヲ待1王スねたゆ伝派んね点過カト治読よにきべ使人スシ都言え阻8割べづえみ注引敷的岳犠眠どそ。 学用イだ医客開ロ供界もぞだ実隆モイヌ務坂ナコヲ権野ろづ初場ぱ低会づぱじ新倒コ化政レ止奮浸猪ッわえづ。形いやリ要帰ほまむだ業領スル必打さ島14巻リ集日ネヘホタ面幅ち写上そぴ円図ムタコモ報使イわざと会催ヤヲ康証をドぶレ盤岡ホハツ作29管しをめ公問懐蓄っさ。来ゆぼあぱ投秋シ語右ぐ身靖かば辛握捕家記ヘワ神岐囲づ毘観メテクツ政73夕罪57需93誌飲査仁さ。 変レめ束球よんま会特ヱコ聞重だ史純ーどる件32浦レぴよゃ上強ネラリロ査従セユヤ専棋光レ作表ひぶ予正ぜーな誉確フス函6報円ス進治ね能営済否雄でわょ。42生型ば着続ア短実ぎおめび前環闘ラヤヲル診均っとにの声公トヱテマ整試椅情久妊舌頃ざとっく。品キチトテ阿国ラら受87世ヲフセリ川86個ーょぼげ危子ヘレカメ無会ぱかへ事通んかて電条ロツ徴商ぶぞそを居暑メ害広せもがり禁応レミヲ応響割壮憶はぱ。 千れンが織財メニ況界ネトレミ学豊フオホシ近月レたやご的罪ょな菱技ちる警栗エセ提89林危氷48参ア説森クキヒヱ薬社ホコエリ負和ルび紀下ケミイ掲歳特ごず扱底ク護木連ちクを各形ばすか。変ぱなれ町7融ヌ街準以タユヘム質裕ぶで遺語俊ぎずょ事金文キ写多山ーゆに歩帯すで会世クぜよ論写ヲ達71林危氷5間続ぎぜび高怠す。 係8青け応著ミ戦条ナヘネカ思79未ぎ算伊をゃ泉人ーづ需説っ畑鹿27軽ラソツ権2促千護ルロナカ開国ケ暴嶋ご池表だ。佐フナ訪麻はてせば勝効をあ医戦画とさわぴ者両すいあ並来んば載食ぴ件友頂業へえぞ魚祝ネラ聞率スコリケ始全ンこび夫出ドふ今布うぎふゅ実克即哉循やしんな。 暮す備54依紀てッん末刊と柔称むてス無府ケイ変壌をぱ汁連フマス海世ヌ中負知問ナヘケ純推ひ読着ヒ言若私軽れ。掲けフむ王本オコ線人をっさ必和断セソヲハ図芸ちかな防長りぶは投新意相ツ並5余セ職岳ぞ端古空援そ。森ヨエチ題5東っ自兄ち暴5近鹿横ト的京ハ安氷ナキ深際ぎ並節くスむの権工ほルせ京49効タムチ処三ぞぴラ済国ずっ文経ヘトミ水分準そが。').split(" ")

fn s3_action_names() raises -> List[String]:
    return String('AbortMultipartUpload CompleteMultipartUpload CopyObject CreateBucket CreateMultipartUpload DeleteBucket DeleteBucketAnalyticsConfiguration DeleteBucketCors DeleteBucketEncryption DeleteBucketIntelligentTieringConfiguration DeleteBucketInventoryConfiguration DeleteBucketLifecycle DeleteBucketMetricsConfiguration DeleteBucketOwnershipControls DeleteBucketPolicy DeleteBucketReplication DeleteBucketTagging DeleteBucketWebsite DeleteObject DeleteObjects DeleteObjectTagging DeletePublicAccessBlock GetBucketAccelerateConfiguration GetBucketAcl GetBucketAnalyticsConfiguration GetBucketCors GetBucketEncryption GetBucketIntelligentTieringConfiguration GetBucketInventoryConfiguration GetBucketLifecycle GetBucketLifecycleConfiguration GetBucketLocation GetBucketLogging GetBucketMetricsConfiguration GetBucketNotification GetBucketNotificationConfiguration GetBucketOwnershipControls GetBucketPolicy GetBucketPolicyStatus GetBucketReplication GetBucketRequestPayment GetBucketTagging GetBucketVersioning GetBucketWebsite GetObject GetObjectAcl GetObjectAttributes GetObjectLegalHold GetObjectLockConfiguration GetObjectRetention GetObjectTagging GetObjectTorrent GetPublicAccessBlock HeadBucket HeadObject ListBucketAnalyticsConfigurations ListBucketIntelligentTieringConfigurations ListBucketInventoryConfigurations ListBucketMetricsConfigurations ListBuckets ListMultipartUploads ListObjects ListObjectsV2 ListObjectVersions ListParts PutBucketAccelerateConfiguration PutBucketAcl PutBucketAnalyticsConfiguration PutBucketCors PutBucketEncryption PutBucketIntelligentTieringConfiguration PutBucketInventoryConfiguration PutBucketLifecycle PutBucketLifecycleConfiguration PutBucketLogging PutBucketMetricsConfiguration PutBucketNotification PutBucketNotificationConfiguration PutBucketOwnershipControls PutBucketPolicy PutBucketReplication PutBucketRequestPayment PutBucketTagging PutBucketVersioning PutBucketWebsite PutObject PutObjectAcl PutObjectLegalHold PutObjectLockConfiguration PutObjectRetention PutObjectTagging PutPublicAccessBlock RestoreObject SelectObjectContent UploadPart UploadPartCopy WriteGetObjectResponse, CreateAccessPoint CreateAccessPointForObjectLambda CreateBucket CreateJob CreateMultiRegionAccessPoint DeleteAccessPoint DeleteAccessPointForObjectLambda DeleteAccessPointPolicy DeleteAccessPointPolicyForObjectLambda DeleteBucket DeleteBucketLifecycleConfiguration DeleteBucketPolicy DeleteBucketReplication DeleteBucketTagging DeleteJobTagging DeleteMultiRegionAccessPoint DeletePublicAccessBlock DeleteStorageLensConfiguration DeleteStorageLensConfigurationTagging DescribeJob DescribeMultiRegionAccessPointOperation GetAccessPoint GetAccessPointConfigurationForObjectLambda GetAccessPointForObjectLambda GetAccessPointPolicy GetAccessPointPolicyForObjectLambda GetAccessPointPolicyStatus GetAccessPointPolicyStatusForObjectLambda GetBucket GetBucketLifecycleConfiguration GetBucketPolicy GetBucketReplication GetBucketTagging GetBucketVersioning GetJobTagging GetMultiRegionAccessPoint GetMultiRegionAccessPointPolicy GetMultiRegionAccessPointPolicyStatus GetMultiRegionAccessPointRoutes GetPublicAccessBlock GetStorageLensConfiguration GetStorageLensConfigurationTagging ListAccessPoints ListAccessPointsForObjectLambda ListJobs ListMultiRegionAccessPoints ListRegionalBuckets ListStorageLensConfigurations PutAccessPointConfigurationForObjectLambda PutAccessPointPolicy PutAccessPointPolicyForObjectLambda PutBucketLifecycleConfiguration PutBucketPolicy PutBucketReplication PutBucketTagging PutBucketVersioning PutJobTagging PutMultiRegionAccessPointPolicy PutPublicAccessBlock PutStorageLensConfiguration PutStorageLensConfigurationTagging SubmitMultiRegionAccessPointRoutes UpdateJobPriority UpdateJobStatus').split(" ")

fn system_words_collection() raises -> List[String]:
    return Path("/usr/share/dict/words").read_text().split("\n")

--- corpora/arabic.txt ---

ثم أسر فمرّ لأداء, مع ومضى فاتّبع قبل. وزارة التّحول عن الى, كان أن وقوعها، الإطلاق الدّفاع, ودول الثالث، لتقليعة الى عن. أم عرض ميناء شواطيء استدعى, تونس بقسوة وإقامة هو نفس, ميناء بتخصيص الإنذار، أم كما. عل العدّ وإيطالي أما, ٣٠ فعل دارت وبعد.
أي بالعمل والقرى قبل. ونتج الجنوب إيو أم. أن ولم الجو واحدة للإتحاد. هاربر العالم جُل بل, النفط الإحتفاظ الأوربيين ثم قام. وبغطاء الأوربيين حتى هو, أسر و شاسعة المؤلّفة, أمدها وإقامة العالم، مع يبق. تم وقامت تكاليف وباستثناء قبل, أدنى بأيدي الدولارات من حيث.
كثيرة الثقيلة ٣٠ هذه. إذ قادة كنقطة الشهير غير. تمهيد مساعدة التجارية ان وقد, واُسدل بأضرار إيطاليا ثم بعض. كما مع أوسع والحزب ولكسمبورغ, جُل قامت باستخدام بـ. بتطويق والكساد والنرويج لها ان, وتم وفرنسا بالولايات ما, خيار فسقط يتبقّ قام و. بل هُزم بريطانيا الأوربيين فصل, عل بعض مئات اتفاق الربيع،, إذ الأحمر مقاومة ومن. حدى ويتّفق أوروبا إذ, خيار بقسوة تحرّك ٣٠ فقد.
بل الأمم للمجهود الشّعبين كلّ. مئات بالرغم يتم أن. من حيث وانتهاءً الأمريكي, تم كلّ نقطة وحرمان الولايات. هو لمّ السيء عشوائية.
من قتيل، وبولندا الأمريكية مكن, هُزم الأولية انتصارهم حين بـ. المنتصر الأهداف كما بل. ذات لم فكانت لعملة المتاخمة. حول بل للصين لإعادة, عرض أن وشعار الانجليزية. مكن حقول القادة كل. جيوب وبعد الشّعبين جعل أم, عل معاملة أفريقيا بعض, والفلبين ويكيبيديا فصل ٣٠.
وعلى الأمور تحت أم. بين أم المضي والفلبين انتصارهم, الأرض الأراضي انه ان, عن أخر وإقامة الشتاء انتباه. وفي تشكيل فرنسا تشيكوسلوفاكيا ان, في وقرى ليركز وقدّموا مدن. بحث الباهضة الإتفاقية مع, حول ٣٠ أدوات بالفشل ويكيبيديا, أثره، بأيدي بـ وتم. مرجع العصبة الحدود على و.
فصل من حصدت دأبوا الأبرياء, ان مرجع الأرواح عدد, هذا دارت الأمم في. إبّان الإقتصادية قبل بل, بين ان كرسي اكتوبر وبلجيكا،. حتى ما الثالث العمليات العالمية, عل بعد أراض الدول قائمة, من إعمار واتّجه للمجهود عدد. في وجهان تطوير ليرتفع دنو, يتم ببعض حاول المجتمع أم.
أوزار المارق الشهير قد بلا, وحرمان ديسمبر إذ تحت. حدى عن شرسة إعلان الفترة. إذ دخول إستيلاء هذه, ٣٠ هذا وترك لأداء. لإعادة جزيرتي باستخدام من لكل. يقوم بالفشل بريطانيا، شيء أي, كانت بقسوة كل دول, الضغوط الصفحات نفس قد. بمحاولة الأثنان ما ذات.
يتم عن واتّجه المنتصر, بل أوزار الأبرياء وبريطانيا كلا. حدى عن فسقط وصافرات, لها لم شرسة وقامت الجنود. أم الأخذ وانهاء بها, مما جيما فكان مقاومة و. المشترك شموليةً كلّ و, التّحول والمانيا بعد بـ. دفّة وبعدما جديداً فعل ثم, قبل وانهاء الهجوم ان, من وقوعها، المزيفة كلا.
كثيرة بمعارضة ان ذات, الا لعملة بمعارضة الإيطالية لم. جدول المؤلّفة حدى من. قام في الأمور ديسمبر الاندونيسية, كانتا وبولندا جُل ثم. بتحدّي البشريةً كلّ بـ, مايو للصين فهرست هذا ثم, فبعد المضي اوروبا ان عدم. بعد قد ميناء وبداية واتّجه, المارق الأوروبية أي أضف. و بأيدي محاولات جُل, تحت اعلان وأكثرها إذ. لم بعد ليركز الفرنسية.

--- corpora/chinese.txt ---

巡済思真報就少健話新能長載画。条護超館講商法何失転絶障報東民政浜六青聞。木送月定枚性校増予体労根援住話広。親氏創再信止園南質作海資著制映制。上徳次証禁込福画給査北州厳真。政噴属要式優芸花季件童門。議南補太権場検数場爺極東年了目物。背園鬱購事園税応駐小雪京安字。標事何式間若学投対継細一数常遣疑住場通。
魚申天旅連社今覚返車交社聞変。決結載面共対開全京誌約森気経報覧失日。住始問願芋火提法決断名身舞込最。廃図万読聞禁天野著技政来閲署心保見。王最通光期行米表視無校更科楽底衆円。但出感考加出不必敬奈族勢件陣市開意軽役済。市重森先式交図更再験成以極広注。理七垂上尾総信会名海宝海開日情村。無規代両明声先想内追書謙代。
捕点改現立野夫読郵育典提要龍毎権軽。性山陣禁欠近秒米長天号連非世。知張書未羊通嗅第和半教前程挑香。不並配恐著決木予家記容容日達株。露急置上趣用選魅住船座陽予共代去相能。左止朝士猶環部時書間切現沢苦案開子決個挿。極極値年昔友祉写原軽毎月現読勝説訴定件圧。評要園任治撤競記飯康下捕意登育読検地倒文。
対構哨武港載中群読病載音。聞国知売日夕処組眠木手要。録視聞早神転帯政五禁安入写出祉覧古片全。海形健読成安設陽権者男座打成素議。義賞堀発政表養効事吉題前造向冬歓立。初大転目政害定育教育日田千岩露。問上件者晴男件出代新占登祭部的学選。中著万点層愛綺載削的止党設却文蒼。式委現析会司泳厳期逆経長。米内却調暮夜大討防備品発問。
可著裏府暮才夕済断塁意証大。江終旅取方中協広指杯罪真松平開。段源有読運問載込表育日望板南容数索蹴。在急動件源辺永内原申面見語幸未真。毎優医負究対無朝止柳式旗亭祈索稿立営。劇子稿今嫁今虫年館能東球見際法名政。司供特媛方相銃望評覧能音台旅。売健誠系民半丸能橋暮堂田合際製経記題関。選南基合録止飾立連役見催五第連代。
属玲全支開手部衝関優亭国変問社環裏。転指育娘狂第権提連準前乗男連計実。力銀残住面告進更暮平護胸面係相補枝。憲舞対限容手健福年局月券談堀。島量極区明説芸豊故勝例部初都共者場。子成戦籍火北保際徳各一判交。志言香察凡飯模崎高色盤研促周問罪評噂盟歩。吾済会断全決変必重前著左検和家空境講。省特場薬受天面掲直転技部八。
士杏評公広第診謙学供氏査開式犠索攻趣義。送度毎省源場読乗選球合門表試毎再記入風嶋。月府躍針治宕号告式切試阪字課比市互速。故話紹谷位文期載水五答稿案埼安情。倉応記子初地行宏果後検祭明国訳。至増綾育除魚時外亭新価択。治官録容心国囲動止氏公券効進月句級宣論全。行野接重身妖話作済帳通際告棋。取援業症載像多文下表計時輸化車属。
雄禁読放訳済覧押悪身象真進体。文臣方事占刺俳立堂止回護公碁質。局民返自必面幻座職必質弾標陽渡計大府。休売担記問空川貯則章阻表者郡森編。化乗感想開私湖押面杯訴多怖。緊物文表野図情給端件置属真能。世参文域挙患甲作際辺強赤懇者部。五決政然児能本原表第独利区搬解。入論日講塾条満働大都別話。約載副得実後必学里場四幅芸弁示北高達。
将原放今恵該設島世稿代社。射第法件金内良無政毎城者佐見所代真弁。棋稿稿工申径走芸組合重泉山字万。頑男無拍超高童宅校全風新川。県場告国好都治中成担未為経要京切王世兵組。所業雄乗問巨図報無渡分写通論踊味転測引。歴許名績断左都席示学小将細。療他繕原謝保選域淡含誤保誤戦究択圧意。者文強弟更権浮地製市任竹斉内童殖死課。
園転掲連経取奥再異助事打人鮎。館聘掲入阪整太内也与先青並。選神情恒済級郵仁紹争施新近他。情打引治欧治型京鋭見池提象提地意読稿。応就説応判仕狙本所由影要軽記続。行置業載役世仁四界骨際桑格極再院小載再燃。義頼白列紹半社選奪試市新醸。成雲川法住下経乗康参日迎力食超施変挙朝民。聞葉性経好玲学体返渡定状容無急大代職翌味。

--- corpora/french.txt ---

At hâs putànt tîmèam. No èssé mêis mèî, tê vïde éuismod sapërét eœs. Né qûï tàle cotidîequê, congue definïebàs çompléctîtur te qùo. Ea méà énim docendi praésent, no probo oblïqûe çœncèptam mea. Ut tàmqùam principes qûi, sàdipscing cônsectétuer çu vim. Vix në sïmul nemôrê torquâtos, stët facete përîculâ eà cum.
Façëte offîçïïs atomœrùm éu prî, at tritàni delenit prô. Qùi volutpàt tîncîdunt et, îus èlît iusto œratîœ ân. Côngûê plâcerat similique sea èt, sîmùl albuçïus în pro. Ei prôbo concéptam rêprimiqùè qui, éu labôre possit his, të ius bruté vérïtus. Vivèndo tràctatos per cu, quîs œportéat ràtiônîbus êx vel.
Usù lobœrtîs rectêqùè an. Pro îd primâ apêriri tractâtos, dicït salutatus mei an. Dissêntiet reprêhendunt his at, mea môvët adipisci éu, quàndô dïctas quo ex. Cû ludûs dôlôrum advêrsârium méî, in eum nèmoré impèdit, prïncîpes urbanitas vœluptatibus sea no. Eù prô mandamus périçùla mâïéstâtis. Ad çlita indôctum mel, ûrbanitas omittàntur vis nœ. Iùs ut tamquam appareat sçrîpsêrit, àn hàs tïncidunt scrïptorém, offïçîis gubergrén nëcéssitàtibùs seâ at.
Dœcéndi ëlïgëndi èi séd. Véniâm nœluissé îd mël. Ad fâllî iudico sit. Facer qûodsi in sit, méi facétë similique at, his vëniam dicûnt menandri tè. Qùândo ëfficïàntur ëî mèà.
Ea méis postéa graèço mei, cu stet cîbœ tamquam ius, îd pro porro assuéverit. Eum irîùre probatus id. Sed éû laùdém aetérno, âgàm omnêsque nêcessïtatibus çû ëœs, vix ex probœ mëliore. Dolœrê philosophia ët cum. No nam nèmœre nonumes.
Alïi verear quî te, ridêns cœpiosaè detraxit an ius. Ad omnës ëleîfênd his, nonumés laborâmus usu et. Pertinax reprîmiqùé eà mel, êx mél quot fâbulas èpîçuri. Eros ludus învîdùnt èum nè, mùtât modus vis et. Sea ât dôctùs dispùtàtiôni, pro ad brute simul tràctâtœs, est cëtèro intéllégàm ïd. Adhuç mundï quî èi, éu veniam aperiam ïùs.
Lùciliûs volùtpat mél in, legeré opœrtèàt sea ne, mêl illud rèqûê labîtur ùt. Ad altêrum vulputate nâm, séd ëripuit lâborês nô. Pri choro dôlorum cu, ut mea îûdîco cetèro cônvêniré. Muçiûs optiôn rèfôrmîdans ad ést, ât mél dœming qualîsqué, nec ôdio molëstîe vïtupératœribus ne.
Solét fuïssét êx mêî, èï nobîs fâçilisis élaborarët ést, ut mël tantas âdvérsarium. Ne unum vœcibus prœdessêt vim, in hïs hàrum traçtatos corrûmpit. Utinam latine maiorum id duœ, eligéndi vitùperâtà disputationi no est. Të élit apériàm séd, pertïnacia omîttàntur et èst. Stet vïdît lobortis has tê, ëi nàm commùné iràcùndiâ phïlosœphîa.
Vis summô êvértitur id, nàtùm fàcer percîpitur ne séa. Eàm promptâ éxpëtendâ nê, hàrum tibîqué sèâ ut. Meîs facîlisi an quî, cû latine àccusatà assuèverit mei. Doctus habémùs ne vél, mèa ét vitàé nostrud appareat, at duis çommodo hïs. In êam quôd malorûm façilïs. Ex vis essé modus çôrrumpit, çum èâ rêpudiandâé signiferumqué, nam nœ réqùè inânï dëlicata.
Vis id œmnïs mâzim, in sït sàpèrét debîtis, qûâestio prodësset tè vîm. An sôléât àdipiscing meî. Sea àn dêtracto repùdiare, id dolorèm placeràt vîtûpêràtoribus sëd. Hàs cœrporâ çœncéptâm no.

--- corpora/georgian.txt ---

ლორემ იფსუმ დოლორ სით ამეთ, ფერრი ცოფიოსაე სედ ეუ, მოლესთიე ფერფეთუა ეუ ვის. მალორუმ ვირთუთე ინ ყუი, ყუო ეა ორნათუს ვითუფერათა. ევერთი ინერმის ან ესთ. აუთემ ნულლამ დისსენთიას ად ეუმ. თემფორ ანიმალ რეფრიმიყუე ინ ჰას, უსუ ფრიმის ელეცთრამ თე, აუთემ აუდიამ ეამ უთ.
ათ მელიორე მოლესთიე დელიცათისსიმი ფრო, ვივენდუმ ინვენირე ეოს ნო. ჰის ეთ ცეთერო დისფუთათიონი, ჰის იდ ლეგერე ლაორეეთ დისფუთანდო, ყუი ეა თათიონ მუნერე ფოსთეა. იუს ეფიცურეი ფაცილისის ომითთანთურ ცუ, ერანთ თრაცთათოს ვის ეი. ურბანითას ინცორრუფთე სით ან.
ნე ომნეს სემფერ ყუო. ეი ეამ მოვეთ ვენიამ არგუმენთუმ, ვერეარ თორყუათოს ელოყუენთიამ ფრი თე. ათ ირაცუნდია გუბერგრენ ფრო, ელით სფლენდიდე ად უსუ. დიცით ლაორეეთ ეხ ეოს, ეამ ათ ირიურე ფეთენთიუმ იმფერდიეთ, ეიუს ფრიმის რეფორმიდანს ვიმ თე. სუმო ნოვუმ მეი ნე. ან ეამ ალიენუმ რეცთეყუე მოლესთიაე.
მეი ნოვუმ სიმულ ეუ, დიცთა ფუთენთ ჰას ეთ. ყუი მალის ლეგერე ეთ. ჰის ცუ ინანი უბიყუე გრაეცე. ინიმიცუს ცონსეცთეთუერ ფერ ეხ, უთ ჯუსთო ფოსსე ფერ. ეა ჰას ინვიდუნთ სცრიფსერით, მეა ფაცერ ფოსსით ეთ. ფრო ათ თორყუათოს ადვერსარიუმ, ვიმ ცუ ნოვუმ ალიყუანდო.
მეი ლაუდემ ყუაესთიო ინცორრუფთე ან. ალიყუიფ რეფორმიდანს ველ თე, ნო ვის ნიჰილ გრაეცი დომინგ, იდ ფოსსით თიმეამ ფერფეთუა ველ. ჰარუმ აეთერნო ფერსეყუერის ეხ ნეც. ცორრუმფით დეფინითიონეს თე ყუი, მუნერე დელიცათისსიმი ეთ ცუმ. მელ ეთ სოლეთ თანთას ინციდერინთ.
ინ ლობორთის ოცურრერეთ ველ, ნო თალე სუმო მუთათ ფერ. ელეცთრამ სიმილიყუე ფრი ან. ეა დიამ აცცუსამუს ფრი. ინ ანიმალ ვულფუთათე ნეც. უთ ფერრი ფოსთეა ვის.
მელ ინ იუდიცო ყუაეყუე მინიმუმ, ად ფუგით დეცორე ლეგიმუს დუო. იუსთო ფერსეცუთი ეთ იუს, ველ თაციმათეს თინციდუნთ მედიოცრითათემ უთ. ვივენდო დელიცათა ცოთიდიეყუე ფერ ცუ. ყუემ ასსუევერით ნეგლეგენთურ ვიმ ეა, მაზიმ რეცუსაბო ნეცესსითათიბუს ფერ ეხ, ეა მეა მეის მუნერე. ნო დუო დიცთა ვენიამ ინერმის.
დიცამ სალუთათუს იდ უსუ, ნეც ნო დენიყუე ელაბორარეთ აფფელლანთურ. ნეც ომნესყუე ცოთიდიეყუე ცომფლეცთითურ ნო. ინ ფერ რებუმ ფერთინახ, ნონუმეს ჰაბემუს ათ ეუმ. ჯუსთო ნოსთრუდ დისფუთანდო ნო ნამ.
ცუ ლაუდემ ვოლუფთათიბუს იუს. ელით ცონთენთიონეს ველ ეი, ინ ნეც ვენიამ ნონუმy. თე სით ულლუმ დესერუისსე არგუმენთუმ. ან სთეთ ოფორთერე ესთ, ფერრი ფორრო დებითის ეუ ცუმ. ეროს ოფთიონ ეხ ფრო, მელ ათ ინანი ინერმის ვივენდუმ.
იდ ვიმ ილლუდ ინთელლეგებათ. ეა ჰის ცეთეროს ვოლუფთათუმ დისფუთათიონი. თე ფრობათუს თემფორიბუს ინთელლეგებათ ნეც. იდ მელ ამეთ სცაევოლა. ყუოდსი ყუაესთიო სფლენდიდე ყუო იდ, ათ უსუ სალე ინთერესსეთ ცომფრეჰენსამ. იდ ეამ დიცამ დელენითი, თე მეა ფორრო ნულლა, ყუო ალიი ენიმ ფრიმა ათ. ნეც ფუთენთ ფეუგიათ ეა, თე მედიოცრემ ფერსეყუერის ვიმ.

--- corpora/greek.txt ---

Λορεμ ιπσθμ δολορ σιτ αμετ, ιλλθμ φορενσιβθσ εα σιτ. Ηαβεο περιcθλα εφφιcιαντθρ cθ vιμ. Περπετθα αδολεσcενσ εθμ θτ, vιμ οδιο δολορεμ ομνεσqθε αν. Εστ cοπιοσαε λιβεραvισσε νε, ιδ μει ελιτ φεθγιατ μεδιοcρεμ.
Ναμ ρεqθε ιμπεδιτ πρινcιπεσ αδ, ατ ναμ εραντ μθνερε ρεγιονε. Ει προ βλανδιτ φαcιλισι αccθσαμθσ, τε εαμ σαλε παρτεμ απεριαμ, εα πριμα ανιμαλ vιμ. Εοσ ετ διcο ομνισ jθστο, εξ ηασ ελιτ μελιθσ vιvενδο. Σεδ ιγνοτα cοντεντιονεσ εθ, νε ιλλθδ ποσσε εστ. Εξ μελ cλιτα ομνιθμ vολθτπατ, μεα ταcιματεσ ελαβοραρετ αδ. Εξερcι εξπετενδα ει μεα, εα ηασ περσιθσ cοτιδιεqθε.
Πθρτο δεσερθντ εvερτιτθρ cθμ cθ. Cλιτα ιθδιcο qθιδαμ νεc ιδ. Μελ νθλλαμ cονcεπταμ ει, ατ προ αδιπισcι ρεπθδιαρε cονστιτθτο. Γραεcε δεβιτισ γλοριατθρ νεc ιδ. Δθο ετιαμ ρεφερρεντθρ τε.
Εοσ ιν οπορτερε σπλενδιδε. Εραντ δομινγ cομπρεηενσαμ εθ σεδ, ιμπετθσ νθσqθαμ ιντερπρεταρισ ετ ηασ, περ αβηορρεαντ μοδερατιθσ νε. Προ πθρτο vιρισ δολορε τε, οπτιον vερτερεμ αδολεσcενσ ιθσ νο. Νο cθμ σθασ δεβιτισ τραcτατοσ, πλαcερατ φαcιλισισ cθ μελ, cασε ιλλθμ θρβανιτασ ετ ιθσ. Απεριρι σcριβεντθρ vελ ιν, νεc αθγθε λαβιτθρ εα.
Cθ πριμα φθισσετ τινcιδθντ περ, vιμ cθ ινανι ελαβοραρετ. Ατ δθο νισλ ηομερο. Νο διcιτ εσσεντ ελειφενδ εθμ, ετ διcτα ιντερπρεταρισ ηισ. Προδεσσετ μνεσαρcηθμ μεδιοcριτατεμ σεα εα, γραεcε φαβθλασ πρι ει. Vιμ αδ vιδε πηιλοσοπηια.
Εστ νε ερατ ριδενσ, προ cθ πθτεντ σεντεντιαε. Ατqθι φαcιλισ ιντελλεγατ αδ ηισ, qθο σθμμο vολθμθσ περσεqθερισ εξ. Μει ιμπεδιτ τηεοπηραστθσ cθ. Ατ αδμοδθμ δενιqθε ιθσ, αν σαεπε προπριαε ιντελλεγατ δθο, ηισ εθ αccθσατα πετεντιθμ. Ιδ σιτ μθνερε ινcορρθπτε. Ποστθλαντ ινcιδεριντ νεc αδ, λαθδεμ cονσθλατθ ιμπερδιετ vιμ τε, vιvενδθμ qθαλισqθε vολθπταρια θτ ναμ.
Εθ vερι ιντερεσσετ vισ. Μοδο βρθτε ιισqθε θτ vισ. Μελ ιδ δεβετ vιτθπερατα ιντερπρεταρισ, μει εροσ qθιδαμ cομπλεcτιτθρ νε, ατ vιταε μαιορθμ cονσεcτετθερ νεc. Ιν δολορθμ φαcιλισισ εοσ, εξ ενιμ λαβοραμθσ ηισ, δοcτθσ απειριαν σεντεντιαε qθο εθ. Σονετ δελιcατισσιμι εοσ νο, σονετ vιταε νο σεα. Εξ σεδ φαcιλισι cορρθμπιτ.
Πριμα τηεοπηραστθσ εα ιθσ. Ατ vελ ατqθι vιδισσε ινvιδθντ. Εαμ cαθσαε οπτιον ελιγενδι αν. Ετ εριπθιτ νομινατι ελαβοραρετ εαμ, τιμεαμ λεγιμθσ πατριοqθε δθο αδ, ετ δθο σενσιβθσ τεμποριβθσ περσεqθερισ.
Αν vιμ οδιο ταλε, σεδ τε ιδqθε τεμποριβθσ. Ηινc γραεcι ασσθεvεριτ μει εθ, vελ ιν περσιθσ ινερμισ περτιναξ, αθτεμ τολλιτ μολεστιαε δθο ατ. Σεντεντιαε τεμποριβθσ ει σεδ, qθι νο μινιμ δολορ qθανδο. Θτ ηισ cονσθλ φθισσετ, vολθμθσ cονσθλατθ σcριβεντθρ vισ αδ. Ειρμοδ ομνιθμ σενσιβθσ ηισ νε, εαμ περσιθσ αντιοπαμ περιcθλισ εα, vισ cασε σολθμ ιντελλεγατ ατ.
Ει τοτα ρεπριμιqθε δελιcατισσιμι vιξ, εξπετενδα vθλπθτατε τινcιδθντ νεc αδ, ιλλθμ σcριπτα περ θτ. Απεριαμ εριπθιτ πρι ει, ετ εοσ πθταντ εqθιδεμ vθλπθτατε. Νεc διcθντ λατινε δεσερθισσε εθ, cθ εαμ μοδθσ qθοδσι ινvιδθντ. Νο μελ μοδθσ cετεροσ νεcεσσιτατιβθσ, πριμα σονετ απειριαν εθμ εξ. Ελιγενδι τηεοπηραστθσ νε qθι, διcατ τεμπορ λαορεετ μελ εα, εα σcριπτα δοcενδι vολθπτθα σιτ. Νο σαλε λθδθσ αππελλαντθρ.

--- corpora/hebrew.txt ---

זאת ולחבר מדריכים את. עוד הגרפים ייִדיש גם, או שמו הרוח חינוך אספרנטו, תנך ספרדית תיקונים ביולוגיה ב. שתי גם אחרים קלאסיים בויקיפדיה, או שתי עסקים תרומה חבריכם. כלל ראשי בחירות לעריכה של, ב מוגש יכול המזנון כתב. החול בחירות בדף על, בה הטבע בקלות האטמוספירה צעד.
רבה מדריכים סוציולוגיה או. גם בקלות מיזמים מועמדים קרן, גם אחר מושגי ומדעים רב־לשוני. ב מפתח זכויות התפתחות מדע, תורת רביעי גם זכר. היום לציין או כתב, שאלות והנדסה בה אנא, את דפים עיצוב כתב. אם שנורו רב־לשוני בקר, על הטבע לחיבור סדר, או כלל מוסיקה הסביבה.
ובמתן והנדסה על תנך, כלים בקרבת מונחים קרן אל, חפש נבחרים אירועים פוליטיקה או. על כדי ערכים לחיבור שינויים. שער בה ברית כלשהו אנגלית. ננקטת וקשקש רבה של, או החלל מיתולוגיה שער, תנך או רוסית באגים שימושיים.
זאת בה תיבת יסוד לערוך, גרמנית קישורים האנציקלופדיה מה אחר, ב נפלו העזרה אנא. והוא התפתחות ויקיפדיה בה אתה. תיבת שאלות טבלאות עזה דת. אל מתן מדעי ניווט ביולי. או רבה אירועים אינטרנט האטמוספירה, פיסול אודות ב כלל.
או ברית דרכה מתן. כלל קהילה ומהימנה על, צ'ט אל הבהרה בקלות הספרות. אחד את החלל סטטיסטיקה, כלשהו טכניים עוד אם, היא אל מונחונים האטמוספירה. אל ציור עסקים בעברית זאת.
תנך או בידור קודמות. אל בקר אחרות פולנית. ביולי ספינות שמו את, בקר שונה ומהימנה ופיתוחה אם. פיסול ערכים המשפט אחר בה.
או מדעי תורת ויקימדיה תנך. עזה אל זקוק ביוטכנולוגיה, על כדור רוסית לחשבון תנך. זכר דת מיזמי ספורט בהשחתה, מה דפים מושגי אדריכלות אחר. אחר היום בגרסה את. על מדעי מדריכים מתן, רביעי לערכים מה סדר. זכר גם זכויות אקטואליה אתנולוגיה.
צ'ט הטבע יכול מושגי של, אל לוח שתפו הראשי. על הסביבה מיוחדים שכל, דת שמו יכול ואמנות. מיזם לעריכת קרימינולוגיה את רבה, רבה ב הגולשות רב־לשוני. את כיצד גרמנית למאמרים רבה.
שכל ב ביוני התפתחות תאולוגיה. על עזרה ותשובות כתב, בהבנה בויקיפדיה כדי בה, קרן בקרבת תקשורת או. שאלות קישורים אתה ב, בה רבה מיזם לימודים. קלאסיים טכנולוגיה מדע על. שמו לחשבון הקנאים סוציולוגיה גם. ניווט משחקים מדע אם. שנתי ספורט בקר גם, החברה אחרונים שימושיים קרן בה.
דפים בארגז שמו או, דת עוד לערך אנציקלופדיה, כדי ארכיאולוגיה ביוטכנולוגיה מה. מפתח שמות מועמדים כלל אם. מתוך ציור לעריכת זאת של, הטבע בארגז ומהימנה או כתב. ביולי משפטית רומנית דת כתב, גם לוח פנאי התפתחות.

--- corpora/hindi.txt ---

कैसे संसाध ज्यादा अंग्रेजी गटकउसि हमेहो। प्रसारन सक्षम पसंद ढांचा देते उदेशीत स्वतंत्रता बाटते उद्योग उपलब्धता दिये हिंदी एछित उदेशीत अनुवाद विवरन सकती सारांश सुस्पश्ट अंग्रेजी मुक्त सारांश प्रसारन हार्डवेर एकएस हैं। लेकिन आधुनिक वर्णित बारे जानते संपादक यन्त्रालय
चुनने मानसिक विकसित जिसे करता। दारी बातसमय प्रेरना दस्तावेज जागरुक है।अभी अनुवाद दौरान होगा समाजो प्रसारन सम्पर्क बीसबतेबोध विषय मेमत बनाकर जागरुक लचकनहि विभाजनक्षमता उसीएक् स्वतंत्र प्रौध्योगिकी बारे तरीके आंतरजाल अमितकुमार विकेन्द्रित खरिदे हीकम यधपि करता। हमेहो। केन्द्रिय व्याख्यान मानसिक बिन्दुओमे अर्थपुर्ण सकती बनाकर सके। उपेक्ष प्रोत्साहित माहितीवानीज्य चुनने संस्था शारिरिक सोफ़्टवेर तरीके देते चिदंश मुखय दिनांक व्यवहार
माध्यम दोषसके बातसमय पसंद वर्णन उन्हे वेबजाल हुएआदि प्रोत्साहित लेने हमारी ऎसाजीस प्राधिकरन पढने पहोच हुआआदी हुएआदि ध्वनि वर्तमान केन्द्रिय पुष्टिकर्ता एवम् पहेला सदस्य आशाआपस स्थापित ७हल बाटते भारतीय हमेहो। क्षमता। मेमत समस्याओ व्याख्या प्रति जाता वैश्विक सुस्पश्ट विभाग वहहर लक्षण सुना प्रमान मेंभटृ विश्लेषण किया चिदंश वैश्विक वास्तविक मुश्किले
लेने बनाना खरिदने पत्रिका कम्प्युटर सुचना नीचे विनिमय सोफ़तवेर निरपेक्ष हुआआदी व्याख्यान सहयोग अधिक उपलब्ध संपुर्ण अंग्रेजी बनाने दिये पहोच। जैसी किएलोग स्वतंत्र बीसबतेबोध दारी विकास खयालात विभाजन
असक्षम प्राथमिक वर्णन बारे संस्था सभिसमज मुश्किले देते दौरान भोगोलिक केन्द्रित लेकिन कैसे कीने विशेष वर्णित अधिकांश ध्येय काम उद्योग निर्देश प्रतिबध हमारि सकते प्रव्रुति पुष्टिकर्ता विवरन भाषए पत्रिका विकेन्द्रियकरण
लक्षण पासपाई प्रसारन रखते उनका आवश्यक आंतरजाल अर्थपुर्ण खरिदे बढाता चाहे सभिसमज दस्तावेज स्वतंत्रता सहयोग कीने प्रेरना मुश्किले लगती प्रेरना लाभान्वित करके(विशेष हिंदी वर्णन विनिमय प्रेरना जानकारी हीकम देने बलवान संपादक परिभाषित मुक्त २४भि ऎसाजीस तकनीकी अधिकांश कराना आपके परस्पर वातावरण सार्वजनिक समस्याए संपुर्ण विषय लेकिन विवरन तरहथा। हमेहो। अधिक लगती पहेला करेसाथ वैश्विक प्रति उपलब्धता विभाजनक्षमता कोहम वर्ष विश्व सम्पर्क बीसबतेबोध जिम्मे प्रव्रुति प्राधिकरन लेकिन रचना सामूहिक ढांचामात्रुभाषा बाजार उशकी
एसेएवं समस्याओ लाभान्वित हैं। मुक्त डाले। किके सहायता विस्तरणक्षमता हीकम ब्रौशर ज्यादा मर्यादित प्राण वेबजाल सभीकुछ एकत्रित सेऔर विकास प्रति विषय उपेक्ष नयेलिए वर्तमान कम्प्युटर पुर्व अथवा देखने अपनि अधिकार परस्पर वास्तविक सादगि मानव असक्षम दिनांक सिद्धांत प्राधिकरन बढाता होभर प्राण लक्ष्य बिन्दुओ सुस्पश्ट सकती
विनिमय कम्प्युटर वार्तालाप थातक खरिदे बहुत जानते सामूहिक निर्माता विभाजनक्षमता चाहे सारांश निरपेक्ष सीमित अथवा भाषाओ गुजरना प्राथमिक अधिकांश अन्तरराष्ट्रीयकरन उनके बनाने अत्यंत कार्यलय रचना उन्हे विषय हमारी दिशामे वेबजाल कार्यकर्ता देने हुआआदी सभिसमज बनाकर गएआप नयेलिए दर्शाता समस्याओ संदेश
बाटते ब्रौशर विभाजन विकसित खण्ड मुक्त खयालात उद्योग काम विवरण अधिकार हमेहो। प्रोत्साहित पडता वातावरण समस्याओ विकेन्द्रित लिए। समजते बहुत आधुनिक अर्थपुर्ण हिंदी आवश्यक प्रदान स्वतंत्रता विकास सके। होभर चुनने मानसिक वातावरण विज्ञान किया पत्रिका मुश्किल समस्याए हिंदी
सुचना आपको विज्ञान समस्याओ बेंगलूर एछित डाले। कार्यकर्ता अमितकुमार जानते सुविधा कार्यकर्ता करके(विशेष होभर एकत्रित व्याख्या देने कार्य सादगि जिम्मे वास्तव व्रुद्धि भाषा २४भि उपेक्ष तकनिकल करके(विशेष उन्हे करती परिभाषित माहितीवानीज्य बढाता खयालात एसलिये सुना जानते विवरन दोषसके सोफ़्टवेर सिद्धांत करके दोषसके आवश्यकत व्याख्यान अपनि सुचना जिम्मे आवश्यकत आंतरकार्यक्षमता बाधा चिदंश

--- corpora/l33t.txt ---

5IM1LaR c0mm4ndz h4D 1T. 1n70 kl1k be 4r3. 45 n33d 1nDeX3D, 4u70m471(4lly y3r. N33d miL4R 83 70p, d@ w1ll w1+hOUT INt3r35+3d n0w. +o joo m0r3 4v41|4b|3, Up c|1ck d0(um3n7 d159l4y3d, 73h, 4|| t0 w1tH (4(h3d 51m1l4r. W17h @n33 f00l! 93t y0, 94g3 WI5h h4D da.
|235u|7z 4v41|4b|3 k@n d@. @R3 da 250m \/\/3b 7|24n5|4735. @$ KWIckLy, 1nd1c473z0r w3b. Vve +H@T wh1(h c@ch3d be. 0vvn3r z3aRc|-| 1T f4q. 7he p@93 p1cz! 4z.
K@n |4unch f34tUr3 f337u|23, 0f. M1t3 b33n d0 0u7, @$ iTz f0|2 r35ul7. 4u70m471(4lly. Be @R3 f00l! De@l. |-|@v3, d@ 4r3 vv3b p|20g|24m, 5|\|4p5)-(07. D0 5peNDInG KWIckLy, 4nd. D0 fOr h7m| Wh3|\| L0Ok1NG, 3nT3R Ph13LD, Up y3r.
0n d1z b4(| d33z, CaN p1>< 5(0u7s |247h3r d0, p4g3, INt3r35+3d m4y 1T. TH4T n33d w17h +h3 83, be @R3 F1ND p49E$ pr3f3|23nc35, d3n MOr3 M4NY qu3ry 17. Why 7h15 r35u|7 0f, @$ 7he p@93 p4g3, |235u|7z, 83 t3H p4g3 f|20n7. 0f73n @8ou+ g3t 83, 47 1iNx v3|2510n pdf, p1x 83 qu3ry |\|0t 717|3z. 70 joo P4935 r35u|7z, 4rE U5Ed m0r3 g00g13 iN, d@ kl1x 73rm5 z3aRcH, c4n. 0t|-|3r k0pYr1t3d t3H d@, 717|3z r35ul7. kvv3r33, 17 f0r.
73x7 l1nx iz 937, +hO53 m1-|-3 0t|-|3r pdf 1+, LINk mIGH+ 94g3s CaN y4. Be pdf d33z l1nx c|1ck, @R n0n-3N9l1sh p4|271cu|4|2 f4q. D1z HAV3 v3r510|\| (ra\/\/1z !=. 0R w3b wh1(h d0wn|04d, @$ 93t M155In9 tHUm841|_, 4r3 0f 4cc355 4lvvAyz tHUm841|_. Da NUMbER 1nd1c473z0r 0u7, 17 h4D 5IM1LaR d0wn|04d. WI5h 7h4n 93t u5, @R3 @$ wIlL NUMbER f1|\|d.
Up 7he 4|50 m47(h z3aRc|-|, 7h3y (4(h3d k0pYr1t3d c@N b3, 7h47 tR4nz|_4t10n @nD 1n. 5O f0r p4g3 h4v3 0f73n. +3's yOU, d0(um3n7 1+ fOr. IN 937 f|20n7 0t|-|3r zp33k3rz, 0u7 Th@t 4b0u7 c0mpu73|2, 83. 0n d0n't 74|<3z v3|2510n H45, 7h3 De@l. 0p710n 17. B33n w17h p@g3z y3r b3, alz0 |21gh7 3n4b|3 kUm 0n.
N0n 1+ Th@t phr0|\/|, 937 15 |4unch 53rv3s |_@n9U493. 1F u\/ c|1ck 0vvn3r, d@t, f00l! 0p710n BuT y@. IN @nD De@l. 51m1l4r. D4 8Ut 94g3s |_@n9U493, No+ be 94g3 w4nN@, 1iNx w1+hOUT 1F w1t. IN +hO53 c0mpu73|2, iTz, +H@T v3ry c0nT3nT, 73h 0n. 1T z3aRcH k4cH3d k0pYr1t3d 4nd, |1nk 4lvvAyz 1T fOr, 45 BuT 51m1l4r r3l473d.
Iz 4r3 73rm5 534|2ch KeYW0rD5, n33d n0n-3N9l1sh 17 +HE, 83 pHinD 5It35 4r3. PHor d159l4y3d, @$ p1x, l3tz f4m1|14r 1nDeX3D, why |7. N33d 34513r d3n y4, M155In9 c0nT3nT, 1T f0r. (0py d33z 5O c4n, 4r3 v3r510n tR@nz|_4t3d 4u70m471c4||y 47.
4nd vv1t 0f73n |_@n9U493 d0. 1F @R3 p@g3 wIlL, pdf b3 HELp m47(h. F4q h7m| l1nx r35u|7z 0R. W1|| l3tz M@Y 1F, |1nk yOU, r33zUltz 0f d1z, LINk 3n4b|3d, g3t +o. 17 kl1x Wh0 |23p|4c3d z33. 0f HAV3 p1cz! 3ng|335h 8Ut, 1F vve p@g3 vv0rx pr3f3|23nc35.
D0nT f00l! pR0dUc+ m4y 1+. L1|\|k tR4nz|_4t10n 4u70m471c4||y u5 4r3, w17h p1cz! k0nt@kt 4z pdf, vv1t L0Ok1NG 0t|-|3r N0+ Up. P1cz! 1nF0, r3zUltz y4 w3b, != u\/ 1nt0 p|20g|24m,. (0py 3N9l1sh t0 c@N. 45 (4(]-[3z d0(um3n7 1PH, 1n p1x |-|@v3 |3tz0rz 4u70m471(4lly. != M4NY 3nT3R 73h, 0u7 F1lt3r f0|2m4771ng 17.

--- csv/.checkoutinfo ---

Mon Jun 3 14:53:31 CEST 2024
URL: https://github.com/mzaks/mojo-csv
Path: csv


--- csv/__init__.mojo ---

from .csv_builder import CsvBuilder
from .csv_table import CsvTable


--- csv/csv_builder.mojo ---

from memory.memory import memcpy
from buffer import Buffer, Dim
from .string_utils import find_indices, contains_any_of, string_from_pointer

alias BufferType = Buffer[DType.int8]
alias CR_CHAR = "\r"
alias CR = ord(CR_CHAR)
alias LF_CHAR = "\n"
alias LF = ord(LF_CHAR)
alias COMMA_CHAR = ","
alias COMMA = ord(COMMA_CHAR)
alias QUOTE_CHAR = '"'
alias QUOTE = UInt8(ord(QUOTE_CHAR))


struct CsvBuilder:
    var _buffer: DTypePointer[DType.uint8]
    var _capacity: Int
    var num_bytes: Int
    var _column_count: Int
    var _elements_count: Int
    var _finished: Bool

    fn __init__(inout self, column_count: Int):
        self._capacity = 1024
        self._buffer = DTypePointer[DType.uint8].alloc(self._capacity)
        self._column_count = column_count
        self._elements_count = 0
        self._finished = False
        self.num_bytes = 0

    fn __init__(inout self, *coulmn_names: StringLiteral):
        self._capacity = 1024
        self._buffer = DTypePointer[DType.uint8].alloc(self._capacity)
        self._elements_count = 0
        self._finished = False
        self.num_bytes = 0

        var column_name_list: VariadicList[StringLiteral] = coulmn_names
        self._column_count = len(column_name_list)
        for i in range(len(column_name_list)):
            self.push(coulmn_names[i])

    fn __del__(owned self):
        if not self._finished:
            self._buffer.free()

    fn push[S: Stringable](inout self, value: S, consider_escaping: Bool = False):
        self.push(str(value), consider_escaping)

    fn push_empty(inout self):
        self.push("", False)

    fn fill_up_row(inout self):
        var num_empty = self._column_count - (self._elements_count % self._column_count)
        if num_empty < self._column_count:
            for _ in range(num_empty):
                self.push_empty()

    fn push(inout self, s: String, consider_escaping: Bool = True):
        if consider_escaping and contains_any_of(
            s, CR_CHAR, LF_CHAR, COMMA_CHAR, QUOTE_CHAR
        ):
            return self.push(QUOTE_CHAR + escape_quotes_in(s) + QUOTE_CHAR, False)

        var size = len(s)
        self._extend_buffer_if_needed(size + 2)
        if self._elements_count > 0:
            if self._elements_count % self._column_count == 0:
                self._buffer.offset(self.num_bytes).store(CR)
                self._buffer.offset(self.num_bytes + 1).store(LF)
                self.num_bytes += 2
            else:
                self._buffer.offset(self.num_bytes).store(COMMA)
                self.num_bytes += 1

        memcpy(self._buffer.offset(self.num_bytes), s.unsafe_ptr(), size)
        s._strref_keepalive()

        self.num_bytes += size
        self._elements_count += 1

    @always_inline
    fn _extend_buffer_if_needed(inout self, size: Int):
        if self.num_bytes + size < self._capacity:
            return
        var new_size = self._capacity
        while new_size < self.num_bytes + size:
            new_size *= 2
        var p = DTypePointer[DType.uint8].alloc(new_size)
        memcpy(p, self._buffer, self.num_bytes)
        self._buffer.free()
        self._capacity = new_size
        self._buffer = p

    fn finish(owned self) -> String:
        self._finished = True
        self.fill_up_row()
        self._buffer.offset(self.num_bytes).store(CR)
        self._buffer.offset(self.num_bytes + 1).store(LF)
        self.num_bytes += 3
        return string_from_pointer(self._buffer, self.num_bytes)


fn escape_quotes_in(s: String) -> String:
    var indices = find_indices(s, QUOTE_CHAR)
    var i_size = len(indices)
    if i_size == 0:
        return s

    var size = len(s._buffer)
    var p_current = DTypePointer(s.unsafe_ptr())
    var p_result = DTypePointer[DType.uint8].alloc(size + i_size)
    var first_index = int(indices[0])
    memcpy(p_result, p_current, first_index)
    p_result.offset(first_index).store(QUOTE)
    var offset = first_index + 1
    for i in range(1, len(indices)):
        var c_offset = int(indices[i - 1])
        var length = int(indices[i]) - c_offset
        memcpy(p_result.offset(offset), p_current.offset(c_offset), length)
        offset += length
        p_result.offset(offset).store(QUOTE)
        offset += 1

    var last_index = int(indices[i_size - 1])
    memcpy(p_result.offset(offset), p_current.offset(last_index), size - last_index)
    return string_from_pointer(p_result, size + i_size)


--- csv/csv_table.mojo ---

from .string_utils import find_indices, string_from_pointer
from algorithm.functional import vectorize
from sys.info import simdwidthof
from sys.intrinsics import compressed_store
from math import iota
from memory import stack_allocation


alias QUOTE = ord('"')
alias COMMA = ord(",")
alias LF = ord("\n")
alias CR = ord("\r")
alias simd_width_u8 = simdwidthof[DType.uint8]()

struct CsvTable[sep: Int = COMMA]:
    var _inner_string: String
    var _starts: List[Int]
    var _ends: List[Int]
    var column_count: Int

    fn __init__(inout self, owned s: String, with_simd: Bool = True):
        self._inner_string = s
        self._starts = List[Int](capacity=10)
        self._ends = List[Int](capacity=10)
        self.column_count = -1
        if with_simd:
            self._simd_parse()
        else:
            self._parse()

    @always_inline
    fn _parse(inout self):
        var length = len(self._inner_string)
        var offset = 0
        var in_double_quotes = False
        self._starts.append(offset)
        while offset < length:
            var c = self._inner_string._buffer[offset]
            if c == QUOTE:
                in_double_quotes = not in_double_quotes
                offset += 1
            elif not in_double_quotes and c == sep:
                self._ends.append(offset)
                offset += 1
                self._starts.append(offset)
            elif not in_double_quotes and c == LF:
                self._ends.append(offset)
                if self.column_count == -1:
                    self.column_count = len(self._ends)
                offset += 1
                self._starts.append(offset)
            elif (
                not in_double_quotes
                and c == CR
                and length > offset + 1
                and self._inner_string._buffer[offset + 1] == LF
            ):
                self._ends.append(offset)
                if self.column_count == -1:
                    self.column_count = len(self._ends)
                offset += 2
                self._starts.append(offset)
            else:
                offset += 1

        if self._inner_string[length - 1] == "\n":
            _ = self._starts.pop()
        else:
            self._ends.append(length)

    @always_inline
    fn _simd_parse(inout self):
        var p = DTypePointer(self._inner_string.unsafe_ptr())
        var string_byte_length = len(self._inner_string)
        var in_quotes = False
        var last_chunk__ends_on_cr = False
        self._starts.append(0)

        @always_inline
        @parameter
        fn find_indicies[simd_width: Int](offset: Int):
            var chars = p.load[width=simd_width](offset)
            var quotes = chars == QUOTE
            var separators = chars == sep
            var lfs = chars == LF
            var all_bits = quotes | separators | lfs
            var crs = chars == CR

            var offsets = iota[DType.uint8, simd_width]()
            var sp: DTypePointer[DType.uint8] = stack_allocation[
                simd_width, UInt8, simd_width
            ]()
            compressed_store(offsets, sp, all_bits)
            var all_len = all_bits.reduce_bit_count()

            for i in range(all_len):
                var index = int(sp.load(i))
                if quotes[index]:
                    in_quotes = not in_quotes
                    continue
                if in_quotes:
                    continue
                var current_offset = index + offset
                var rs_compensation: Int
                if index > 0:
                    rs_compensation = int(lfs[index] & crs[index - 1])
                else:
                    rs_compensation = int(lfs[index] & last_chunk__ends_on_cr)
                self._ends.append(current_offset - rs_compensation)
                self._starts.append(current_offset + 1)
                if self.column_count == -1 and lfs[index]:
                    self.column_count = len(self._ends)
            last_chunk__ends_on_cr = crs[simd_width - 1]

        vectorize[find_indicies, simd_width_u8](string_byte_length)
        if self._inner_string[string_byte_length - 1] == "\n":
            _ = self._starts.pop()
        else:
            self._ends.append(string_byte_length)

    fn get(self, row: Int, column: Int) -> String:
        if column >= self.column_count:
            return ""

        var index = self.column_count * row + column
        if index >= len(self._ends):
            return ""

        if (
            self._inner_string[self._starts[index]] == '"'
            and self._inner_string[self._ends[index] - 1] == '"'
        ):
            var start = self._starts[index] + 1
            var length = (self._ends[index] - 1) - start
            var p1 = Pointer[UInt8].alloc(length + 1)
            memcpy(p1, DTypePointer(self._inner_string.unsafe_ptr()).offset(start), length)
            var _inner_string = string_from_pointer(p1, length + 1)
            var quote_indices = find_indices(_inner_string, '"')
            var quotes_count = len(quote_indices)
            if quotes_count == 0 or quotes_count & 1 == 1:
                return _inner_string

            var p = DTypePointer(_inner_string.unsafe_ptr())
            var length2 = length - (quotes_count >> 1)
            var p2 = Pointer[UInt8].alloc(length2 + 1)
            var offset2 = 0
            memcpy(p2, p, int(quote_indices[0]))
            offset2 += int(quote_indices[0])

            for i in range(2, quotes_count, 2):
                var start = int(quote_indices[i - 1])
                var size = int(quote_indices[i]) - start
                memcpy(p2.offset(offset2), p.offset(start), size)
                offset2 += size
            var last = int(quote_indices[quotes_count - 1])
            memcpy(p2.offset(offset2), p.offset(last), length - last)
            _inner_string._strref_keepalive()
            return string_from_pointer(p2, length - (quotes_count >> 1) + 1)

        return self._inner_string[self._starts[index] : self._ends[index]]

    fn row_count(self) -> Int:
        return len(self._starts) // self.column_count


--- csv/string_utils.mojo ---

from algorithm.functional import vectorize
from sys.info import simdwidthof
from sys.intrinsics import compressed_store
# from math import iota, reduce_bit_count, any_true
from math import iota
from memory import stack_allocation
from time import now
from collections.vector import InlinedFixedVector

alias simd_width_i8 = simdwidthof[DType.int8]()

fn vectorize_and_exit[simd_width: Int, workgroup_function: fn[i: Int](Int) capturing -> Bool](size: Int):
    var loops = size // simd_width
    for i in range(loops):
        if workgroup_function[simd_width](i * simd_width):
            return
    
    var rest = size & (simd_width - 1)
    @parameter
    if simd_width >= 64:
        if rest >= 32:
            if workgroup_function[32](size - rest):
                return
            rest -= 32
    @parameter
    if simd_width >= 32:
        if rest >= 16:
            if workgroup_function[16](size - rest):
                return
            rest -= 16
    @parameter
    if simd_width >= 16:
        if rest >= 8:
            if workgroup_function[8](size - rest):
                return
            rest -= 8
    @parameter
    if simd_width >= 8:
        if rest >= 4:
            if workgroup_function[4](size - rest):
                return
            rest -= 4
    @parameter
    if simd_width >= 4:
        if rest >= 2:
            if workgroup_function[2](size - rest):
                return
            rest -= 2

    if rest == 1:
        _= workgroup_function[1](size - rest)


fn find_indices(s: String, c: String) -> List[UInt64]:
    var size = len(s)
    var result = List[UInt64]()
    var char = UInt8(ord(c))
    var p = DTypePointer(s.unsafe_ptr())

    @parameter
    fn find[simd_width: Int](offset: Int):
        @parameter
        if simd_width == 1:
            if p.offset(offset).load() == char:
                return result.append(offset)
        else:
            var chunk = p.load[width=simd_width](offset)
            var occurrence = chunk == char
            var offsets = iota[DType.uint64, simd_width]() + offset
            var occurrence_count = occurrence.reduce_bit_count()
            var current_len = len(result)
            result.reserve(current_len + occurrence_count)
            result.resize(current_len + occurrence_count, 0)
            compressed_store(offsets, DTypePointer[DType.uint64](result.data).offset(current_len), occurrence)

    vectorize[find, simd_width_i8](size)
    return result


fn occurrence_count(s: String, *c: String) -> Int:
    var size = len(s)
    var result = 0
    var chars = InlinedFixedVector[UInt8](len(c))
    for i in range(len(c)):
        chars.append(UInt8(ord(c[i])))
    var p = DTypePointer(s.unsafe_ptr())

    @parameter
    fn find[simd_width: Int](offset: Int):
        @parameter
        if simd_width == 1:
            for i in range(len(chars)):
                var char = chars[i]
                if p.offset(offset).load() == char:
                    result += 1
                    return
        else:
            var chunk = p.load[width=simd_width](offset)

            var occurrence = SIMD[DType.bool, simd_width](False)
            for i in range(len(chars)):
                occurrence |= chunk == chars[i]
            var occurrence_count = occurrence.reduce_bit_count()
            result += occurrence_count

    vectorize[find, simd_width_i8](size)
    return result


fn contains_any_of(s: String, *c: String) -> Bool:
    var size = len(s)
    # var c_list: VariadicListMem[String] = c
    var chars = InlinedFixedVector[UInt8](len(c))
    for i in range(len(c)):
        chars.append(UInt8(ord(c[i])))
    var p = DTypePointer(s.unsafe_ptr())
    var flag = False

    @parameter
    fn find[simd_width: Int](i: Int) -> Bool:
        var chunk = p.load[width=simd_width]()
        p = p.offset(simd_width)
        for i in range(len(chars)):
            var occurrence = chunk == chars[i]
            if occurrence.reduce_or():
                flag = True
                return flag
        return False

    vectorize_and_exit[simd_width_i8, find](size)

    return flag


@always_inline
fn string_from_pointer(p: DTypePointer[DType.uint8], length: Int) -> String:
    # Since Mojo 0.5.0 the pointer needs to provide a 0 terminated byte string
    p.store(length - 1, 0)
    return String(p, length)


fn print_v(v: List[UInt64]):
    print("(" +  str(len(v)) + ")[")
    for i in range(len(v)):
        var end = ", " if i < len(v) - 1 else "]\n"
        print(v[i], end=end)


--- generic_dict/__init__.mojo ---

from .dict import Dict
from .keys_container import Keyable, KeysBuilder
from .multi_dict import MultiDict
from .sparse_array import SparseArray

--- generic_dict/ahasher.mojo ---

# This code is based on https://github.com/tkaitchuck/aHash

from bit import rotate_bits_left, byte_swap
from .keys_container import KeyRef

alias U256 = SIMD[DType.uint64, 4]
alias U128 = SIMD[DType.uint64, 2]
alias MULTIPLE = 6364136223846793005
alias ROT = 23


@always_inline
fn folded_multiply(s: UInt64, by: UInt64) -> UInt64:
    var b1 = s * byte_swap(by)
    var b2 = byte_swap(s) * (~by)
    return b1 ^ byte_swap(b2)


@always_inline
fn read_small(data: DTypePointer[DType.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[DType.uint64]()
            return U128(a, b)
        else:
            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:
        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):
        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 update(inout self, new_data: UInt64):
        self.buffer = folded_multiply(new_data ^ self.buffer, MULTIPLE)

    @always_inline
    fn large_update(inout self, new_data: U128):
        var combined = folded_multiply(
            new_data[0] ^ self.extra_keys[0], new_data[1] ^ self.extra_keys[1]
        )
        self.buffer = rotate_bits_left[ROT]((self.buffer + self.pad) ^ combined)
    
    @always_inline
    fn short_finish(self) -> UInt64:
        return self.buffer + self.pad

    @always_inline
    fn finish(self) -> UInt64:
        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: DTypePointer[DType.uint8], length: Int):
        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)

@always_inline
fn ahash(s: KeyRef) -> UInt64:
    var length = s.size
    var b = s.pointer
    var hasher = AHasher(U256(0, 0, 0, 0))

    if length > 8:
        hasher.write(b, length)
    else:
        var value = read_small(b, length)
        hasher.buffer = folded_multiply(value[0] ^ hasher.buffer, value[1] ^ hasher.extra_keys[1])
        hasher.pad = hasher.pad + length
    
    return hasher.finish()


--- generic_dict/dict.mojo ---

from bit import pop_count, bit_width
from memory import memset_zero, memcpy
from .key_eq import eq
from .keys_container import KeysContainer, KeyRef, Keyable
from .ahasher import ahash
from .single_key_builder import SingleKeyBuilder
 
struct Dict[
    V: CollectionElement, 
    hash: fn(KeyRef) -> UInt64 = ahash,
    KeyCountType: DType = DType.uint32,
    KeyOffsetType: DType = DType.uint32,
    destructive: Bool = True,
    caching_hashes: Bool = True,
](Sized):
    var keys: KeysContainer[KeyOffsetType]
    var key_hashes: DTypePointer[KeyCountType]
    var values: List[V]
    var slot_to_index: DTypePointer[KeyCountType]
    var deleted_mask: DTypePointer[DType.uint8]
    var count: Int
    var capacity: Int
    var key_builder: SingleKeyBuilder

    fn __init__(inout self, capacity: Int = 16):
        constrained[
            KeyCountType == DType.uint8 or 
            KeyCountType == DType.uint16 or 
            KeyCountType == DType.uint32 or 
            KeyCountType == DType.uint64,
            "KeyCountType needs to be an unsigned integer"
        ]()
        self.count = 0
        if capacity <= 8:
            self.capacity = 8
        else:
            var icapacity = Int64(capacity)
            self.capacity = capacity if pop_count(icapacity) == 1 else
                            1 << int(bit_width(icapacity))
        self.keys = KeysContainer[KeyOffsetType](capacity)
        self.key_builder = SingleKeyBuilder()
        @parameter
        if caching_hashes:
            self.key_hashes = DTypePointer[KeyCountType].alloc(self.capacity)
        else:
            self.key_hashes = DTypePointer[KeyCountType].alloc(0)
        self.values = List[V](capacity=capacity)
        self.slot_to_index = DTypePointer[KeyCountType].alloc(self.capacity)
        memset_zero(self.slot_to_index, self.capacity)
        @parameter
        if destructive:
            self.deleted_mask = DTypePointer[DType.uint8].alloc(self.capacity >> 3)
            memset_zero(self.deleted_mask, self.capacity >> 3)
        else:
            self.deleted_mask = DTypePointer[DType.uint8].alloc(0)            

    fn __copyinit__(inout self, existing: Self):
        self.count = existing.count
        self.capacity = existing.capacity
        self.keys = existing.keys
        self.key_builder = self.key_builder
        @parameter
        if caching_hashes:
            self.key_hashes = DTypePointer[KeyCountType].alloc(self.capacity)
            memcpy(self.key_hashes, existing.key_hashes, self.capacity)
        else:
            self.key_hashes = DTypePointer[KeyCountType].alloc(0)
        self.values = existing.values
        self.slot_to_index = DTypePointer[KeyCountType].alloc(self.capacity)
        memcpy(self.slot_to_index, existing.slot_to_index, self.capacity)
        @parameter
        if destructive:
            self.deleted_mask = DTypePointer[DType.uint8].alloc(self.capacity >> 3)
            memcpy(self.deleted_mask, existing.deleted_mask, self.capacity >> 3)
        else:
            self.deleted_mask = DTypePointer[DType.uint8].alloc(0)

    fn __moveinit__(inout self, owned existing: Self):
        self.count = existing.count
        self.capacity = existing.capacity
        self.keys = existing.keys^
        self.key_builder = existing.key_builder^
        self.key_hashes = existing.key_hashes
        self.values = existing.values^
        self.slot_to_index = existing.slot_to_index
        self.deleted_mask = existing.deleted_mask

    fn __del__(owned self):
        self.slot_to_index.free()
        self.deleted_mask.free()
        self.key_hashes.free()

    fn __len__(self) -> Int:
        return self.count

    @always_inline
    fn __contains__[T: Keyable](inout self, key: T) -> Bool:
        try:
            self.key_builder.reset()
            key.accept(self.key_builder)
            var key_ref = self.key_builder.get_key()
            return self._find_key_index(key_ref) != 0
        except:
            return False

    fn put[T: Keyable](inout self, key: T, value: V) raises -> Bool:
        """Return True when value is inserted and not updated."""
        if self.count / self.capacity >= 0.87:
            self._rehash()
        key.accept(self.keys)
        self.keys.end_key()
        var key_ref = self.keys.get_last()

        var key_hash = hash(key_ref).cast[KeyCountType]()
        var modulo_mask = self.capacity - 1
        var slot = int(key_hash & modulo_mask)
        while True:
            var key_index = int(self.slot_to_index.load(slot))
            if key_index == 0:
                @parameter
                if caching_hashes:
                    self.key_hashes.store(slot, key_hash)
                self.values.append(value)
                self.count += 1
                self.slot_to_index.store(slot, SIMD[KeyCountType, 1](self.keys.count))
                return True
            @parameter
            if caching_hashes:
                var other_key_hash = self.key_hashes[slot]
                if other_key_hash == key_hash:
                    var other_key = self.keys[key_index - 1]
                    if eq(other_key, key_ref):
                        self.values[key_index - 1] = value # replace value
                        self.keys.drop_last()
                        @parameter
                        if destructive:
                            if self._is_deleted(key_index - 1):
                                self.count += 1
                                self._not_deleted(key_index - 1)
                                return True
                        return False
            else:
                var other_key = self.keys[key_index - 1]
                if eq(other_key, key_ref):
                    self.values[key_index - 1] = value # replace value
                    self.keys.drop_last()
                    @parameter
                    if destructive:
                        if self._is_deleted(key_index - 1):
                            self.count += 1
                            self._not_deleted(key_index - 1)
                            return True
                    return False
            
            slot = (slot + 1) & modulo_mask

    @always_inline
    fn _is_deleted(self, index: Int) -> Bool:
        var offset = index >> 3
        var bit_index = index & 7
        return self.deleted_mask.offset(offset).load() & (1 << bit_index) != 0

    @always_inline
    fn _deleted(self, index: Int):
        var offset = index >> 3
        var bit_index = index & 7
        var p = self.deleted_mask.offset(offset)
        var mask = p.load()
        p.store(mask | (1 << bit_index))
    
    @always_inline
    fn _not_deleted(self, index: Int):
        var offset = index >> 3
        var bit_index = index & 7
        var p = self.deleted_mask.offset(offset)
        var mask = p.load()
        p.store(mask & ~(1 << bit_index))

    @always_inline
    fn _rehash(inout self) raises:
        var old_slot_to_index = self.slot_to_index
        var old_capacity = self.capacity
        self.capacity <<= 1
        var mask_capacity = self.capacity >> 3
        self.slot_to_index = DTypePointer[KeyCountType].alloc(self.capacity)
        memset_zero(self.slot_to_index, self.capacity)
        
        var key_hashes = self.key_hashes
        @parameter
        if caching_hashes:
            key_hashes = DTypePointer[KeyCountType].alloc(self.capacity)
            
        @parameter
        if destructive:
            var deleted_mask = DTypePointer[DType.uint8].alloc(mask_capacity)
            memset_zero(deleted_mask, mask_capacity)
            memcpy(deleted_mask, self.deleted_mask, old_capacity >> 3)
            self.deleted_mask.free()
            self.deleted_mask = deleted_mask

        var modulo_mask = self.capacity - 1
        for i in range(old_capacity):
            if old_slot_to_index[i] == 0:
                continue
            var key_hash = SIMD[KeyCountType, 1](0)
            @parameter
            if caching_hashes:
                key_hash = self.key_hashes[i]
            else:
                key_hash = hash(self.keys[int(old_slot_to_index[i] - 1)]).cast[KeyCountType]()

            var slot = int(key_hash & modulo_mask)

            while True:
                var key_index = int(self.slot_to_index.load(slot))
                if key_index == 0:
                    self.slot_to_index.store(slot, old_slot_to_index[i])
                    break
                else:
                    slot = (slot + 1) & modulo_mask
            @parameter
            if caching_hashes:
                key_hashes[slot] = key_hash  
        
        @parameter
        if caching_hashes:
            self.key_hashes.free()
            self.key_hashes = key_hashes
        old_slot_to_index.free()

    @always_inline
    fn get[T: Keyable](inout self, key: T, default: V) raises -> V:
        self.key_builder.reset()
        key.accept(self.key_builder)
        var key_ref = self.key_builder.get_key()
        var key_index = self._find_key_index(key_ref)
        if key_index == 0:
            return default
        @parameter
        if destructive: 
            if self._is_deleted(key_index - 1):
                return default
        return self.values[key_index - 1]        

    fn delete[T: Keyable](inout self, key: T) raises:
        @parameter
        if not destructive:
            return

        self.key_builder.reset()
        key.accept(self.key_builder)
        var key_ref = self.key_builder.get_key()
        var key_index = self._find_key_index(key_ref)
        if key_index == 0:
            return
        if not self._is_deleted(key_index - 1):
            self.count -= 1
        self._deleted(key_index - 1)

    fn clear(inout self):
        self.values.clear()
        self.keys.clear()
        memset_zero(self.slot_to_index, self.capacity)
        @parameter
        if destructive:
            memset_zero(self.deleted_mask, self.capacity >> 3)
        self.count = 0

    fn _find_key_index(self, key_ref: KeyRef) raises -> Int:
        var key_hash = hash(key_ref).cast[KeyCountType]()
        var modulo_mask = self.capacity - 1
        var slot = int(key_hash & modulo_mask)
        while True:
            var key_index = int(self.slot_to_index.load(slot))
            if key_index == 0:
                return key_index
            @parameter
            if caching_hashes:
                var other_key_hash = self.key_hashes[slot]
                if key_hash == other_key_hash:
                    var other_key = self.keys[key_index - 1]
                    if eq(other_key, key_ref):
                        return key_index
            else:
                var other_key = self.keys[key_index - 1]
                if eq(other_key, key_ref):
                    return key_index
            slot = (slot + 1) & modulo_mask


    fn debug(self) raises:
        print("Dict count:", self.count, "and capacity:", self.capacity)
        print("KeyMap:")
        for i in range(self.capacity):
            var end = ", " if i < self.capacity - 1 else "\n"
            print(self.slot_to_index.load(i), end=end)
        print("Keys:")
        self.keys.print_keys()
        @parameter
        if caching_hashes:
            print("KeyHashes:")
            for i in range(self.capacity):
                var end = ", " if i < self.capacity - 1 else "\n"
                if self.slot_to_index.load(i) > 0:
                    print(self.key_hashes.load(i), end=end)
                else:
                    print(0, end=end)


--- generic_dict/key_eq.mojo ---

from .keys_container import KeyRef

@always_inline
fn eq(a: KeyRef, b: KeyRef) -> Bool:
    var l = a.size
    if l != b.size:
        return False
    var p1 = a.pointer
    var p2 = b.pointer
    var offset = 0
    alias step = 16
    while l - offset >= step:
        var unequal = p1.load[width=step](offset) != p2.load[width=step](offset)
        if unequal.reduce_or():
            return False
        offset += step
    while l - offset > 0:
        if p1.load(offset) != p2.load(offset):
            return False
        offset += 1
    return True


--- generic_dict/keys_container.mojo ---

from collections.vector import InlinedFixedVector

trait Keyable:
    fn accept[T: KeysBuilder](self, inout keys_builder: T): ...

alias lookup = String("0123456789abcdef")

@value
struct KeyRef(Stringable):
    var pointer: DTypePointer[DType.uint8]
    var size: Int

    fn __str__(self) -> String:
        var result = String("(") + str(self.size) + (")")
        for i in range(self.size):
            result += lookup[int(self.pointer.load(i) >> 4)]
            result += lookup[int(self.pointer.load(i) & 0xf)]
        return result

trait KeysBuilder:
    fn add[T: DType, size: Int](inout self, value: SIMD[T, size]): ...
    fn add_buffer[T: DType](inout self, pointer: DTypePointer[T], size: Int): ...

struct KeysContainer[KeyEndType: DType = DType.uint32](Sized, KeysBuilder):
    var keys: DTypePointer[DType.uint8]
    var allocated_bytes: Int
    var keys_end: DTypePointer[KeyEndType]
    var count: Int
    var capacity: Int
    var key_size: Int

    fn __init__(inout self, capacity: Int):
        constrained[
            KeyEndType == DType.uint8 or 
            KeyEndType == DType.uint16 or 
            KeyEndType == DType.uint32 or 
            KeyEndType == DType.uint64,
            "KeyEndType needs to be an unsigned integer"
        ]()
        self.allocated_bytes = capacity << 3
        self.keys = DTypePointer[DType.uint8].alloc(self.allocated_bytes)
        self.keys_end = DTypePointer[KeyEndType].alloc(capacity)
        self.count = 0
        self.capacity = capacity
        self.key_size = 0

    fn __copyinit__(inout self, existing: Self):
        self.allocated_bytes = existing.allocated_bytes
        self.count = existing.count
        self.capacity = existing.capacity
        self.key_size = existing.key_size
        self.keys = DTypePointer[DType.uint8].alloc(self.allocated_bytes)
        memcpy(self.keys, existing.keys, self.allocated_bytes)
        self.keys_end = DTypePointer[KeyEndType].alloc(self.allocated_bytes)
        memcpy(self.keys_end, existing.keys_end, self.capacity)

    fn __moveinit__(inout self, owned existing: Self):
        self.allocated_bytes = existing.allocated_bytes
        self.count = existing.count
        self.capacity = existing.capacity
        self.key_size = existing.key_size
        self.keys = existing.keys
        self.keys_end = existing.keys_end

    fn __del__(owned self):
        self.keys.free()
        self.keys_end.free()

    @always_inline  
    fn add[T: DType, size: Int](inout self, value: SIMD[T, size]):
        var prev_end = 0 if self.count == 0 else self.keys_end[self.count - 1]
        var key_length = size * T.sizeof()
        var old_key_size = self.key_size
        self.key_size += key_length
        var new_end = prev_end + self.key_size
        
        var needs_realocation = False
        while new_end > self.allocated_bytes:
            self.allocated_bytes += self.allocated_bytes >> 1
            needs_realocation = True

        if needs_realocation:
            var keys = DTypePointer[DType.uint8].alloc(self.allocated_bytes)
            memcpy(keys, self.keys, int(prev_end) + old_key_size)
            self.keys.free()
            self.keys = keys
        
        self.keys.store(prev_end + old_key_size, bitcast[DType.uint8, size * T.sizeof()](value))

    @always_inline
    fn add_buffer[T: DType](inout self, pointer: DTypePointer[T], size: Int):
        var prev_end = 0 if self.count == 0 else self.keys_end[self.count - 1]
        var key_length = size * T.sizeof()
        var old_key_size = self.key_size
        self.key_size += key_length
        var new_end = prev_end + self.key_size
        
        var needs_realocation = False
        while new_end > self.allocated_bytes:
            self.allocated_bytes += self.allocated_bytes >> 1
            needs_realocation = True

        if needs_realocation:
            var keys = DTypePointer[DType.uint8].alloc(self.allocated_bytes)
            memcpy(keys, self.keys, int(prev_end) + old_key_size)
            self.keys.free()
            self.keys = keys
        
        memcpy(self.keys.offset(prev_end + old_key_size), pointer.bitcast[DType.uint8](), key_length)

    @always_inline
    fn end_key(inout self):
        var prev_end = 0 if self.count == 0 else self.keys_end[self.count - 1]
        var count = self.count + 1
        if count >= self.capacity:
            var new_capacity = self.capacity + (self.capacity >> 1)
            var keys_end = DTypePointer[KeyEndType].alloc(self.allocated_bytes)
            memcpy(keys_end, self.keys_end, self.capacity)
            self.keys_end.free()
            self.keys_end = keys_end
            self.capacity = new_capacity

        self.keys_end.store(self.count, prev_end + self.key_size)
        self.count = count
        self.key_size = 0

    @always_inline
    fn drop_last(inout self):
        self.count -= 1

    @always_inline
    fn get_last(self) raises -> KeyRef:
        return self.get(self.count - 1)

    @always_inline
    fn get(self, index: Int) raises -> KeyRef:
        if index < 0 or index >= self.count:
            raise "Invalid index"
        var start = 0 if index == 0 else int(self.keys_end[index - 1])
        var length = int(self.keys_end[index]) - start
        return KeyRef(self.keys.offset(start), length)

    @always_inline
    fn clear(inout self):
        self.count = 0

    @always_inline
    fn __getitem__(self, index: Int) raises -> KeyRef:
        return self.get(index)

    @always_inline
    fn __len__(self) -> Int:
        return self.count

    fn print_keys(self) raises:
        print("(" + str(self.count) + ")[")
        for i in range(self.count):
            var end = ", " if i < self.capacity - 1 else "]\n"
            print(self[i], end=end)


--- generic_dict/multi_dict.mojo ---

from .ahasher import ahash
from .key_eq import eq
from .keys_container import KeyRef, KeysContainer
from .single_key_builder import SingleKeyBuilder
from .sparse_array import SparseArray
from bit import pop_count, bit_width

@value
struct _ValuesIter[
    list_mutability: Bool, //,
    T: CollectionElement,
    NextKeyCountType: DType,
    list_lifetime: AnyLifetime[list_mutability].type,
]:

    alias list_type = List[T]

    var current_index: Optional[Int]
    var next_index: Optional[Int]
    var values: Reference[Self.list_type, list_lifetime]
    var next_values: Reference[Self.list_type, list_lifetime]
    var next_next_values_index: Reference[SparseArray[NextKeyCountType], list_lifetime]
    var first: Bool

    fn __iter__(self) -> Self:
        return self

    fn __next__(
        inout self,
    ) -> Reference[T, list_lifetime]:
        var element = self.values[].__get_ref(self.current_index.or_else(0)) if self.first else self.next_values[].__get_ref(self.current_index.or_else(0))
        self.first = False
        self.current_index = self.next_index
        var next = self.next_next_values_index[].get(self.current_index.or_else(-1))
        self.next_index = Optional(int(next.or_else(-1))) if next else None
        return element[]

    fn __len__(self) -> Int:
        if not self.current_index:
            return 0
        if not self.next_index:
            return 1
        return 2

struct MultiDict[
    V: CollectionElement, 
    hash: fn(KeyRef) -> UInt64 = ahash,
    KeyCountType: DType = DType.uint32,
    NextKeyCountType: DType = DType.uint16,
    KeyOffsetType: DType = DType.uint32,
    caching_hashes: Bool = True,
](Sized):
    var keys: KeysContainer[KeyOffsetType]
    var key_hashes: DTypePointer[KeyCountType]
    var values: List[V]
    var next_values_index: SparseArray[NextKeyCountType]
    var next_values: List[V]
    var next_next_values_index: SparseArray[NextKeyCountType]
    var slot_to_index: DTypePointer[KeyCountType]
    var count: Int
    var capacity: Int
    var key_builder: SingleKeyBuilder

    fn __init__(inout self, capacity: Int = 16):
        constrained[
            KeyCountType == DType.uint8 or 
            KeyCountType == DType.uint16 or 
            KeyCountType == DType.uint32 or 
            KeyCountType == DType.uint64,
            "KeyCountType needs to be an unsigned integer"
        ]()
        constrained[
            NextKeyCountType == DType.uint8 or 
            NextKeyCountType == DType.uint16 or 
            NextKeyCountType == DType.uint32 or 
            NextKeyCountType == DType.uint64,
            "NextKeyCountType needs to be an unsigned integer"
        ]()
        self.count = 0
        if capacity <= 8:
            self.capacity = 8
        else:
            var icapacity = Int64(capacity)
            self.capacity = capacity if pop_count(icapacity) == 1 else
                            1 << int(bit_width(icapacity))
        self.keys = KeysContainer[KeyOffsetType](capacity)
        self.key_builder = SingleKeyBuilder()
        @parameter
        if caching_hashes:
            self.key_hashes = DTypePointer[KeyCountType].alloc(self.capacity)
        else:
            self.key_hashes = DTypePointer[KeyCountType].alloc(0)
        self.values = List[V](capacity=capacity)
        self.slot_to_index = DTypePointer[KeyCountType].alloc(self.capacity)
        memset_zero(self.slot_to_index, self.capacity)
        #TODO: Think about having an optional here or an empty List
        self.next_values = List[V]()
        self.next_values_index = SparseArray[NextKeyCountType]()
        self.next_next_values_index = SparseArray[NextKeyCountType]()

    fn __copyinit__(inout self, existing: Self):
        self.count = existing.count
        self.capacity = existing.capacity
        self.keys = existing.keys
        self.key_builder = self.key_builder
        @parameter
        if caching_hashes:
            self.key_hashes = DTypePointer[KeyCountType].alloc(self.capacity)
            memcpy(self.key_hashes, existing.key_hashes, self.capacity)
        else:
            self.key_hashes = DTypePointer[KeyCountType].alloc(0)
        self.values = existing.values
        self.slot_to_index = DTypePointer[KeyCountType].alloc(self.capacity)
        memcpy(self.slot_to_index, existing.slot_to_index, self.capacity)
        self.next_values = existing.next_values
        self.next_values_index = existing.next_values_index 
        self.next_next_values_index = existing.next_next_values_index 

    fn __moveinit__(inout self, owned existing: Self):
        self.count = existing.count
        self.capacity = existing.capacity
        self.keys = existing.keys^
        self.key_builder = existing.key_builder^
        self.key_hashes = existing.key_hashes
        self.values = existing.values^
        self.slot_to_index = existing.slot_to_index
        self.next_values = existing.next_values^
        self.next_values_index = existing.next_values_index^
        self.next_next_values_index = existing.next_next_values_index^

    fn __del__(owned self):
        self.slot_to_index.free()
        self.key_hashes.free()

    fn __len__(self) -> Int:
        return self.count

    fn put[T: Keyable](inout self, key: T, value: V) raises:
        if self.count / self.capacity >= 0.87:
            self._rehash()
        key.accept(self.keys)
        self.keys.end_key()
        var key_ref = self.keys.get_last()

        var key_hash = hash(key_ref).cast[KeyCountType]()
        var modulo_mask = self.capacity - 1
        var slot = int(key_hash & modulo_mask)
        while True:
            var key_index = int(self.slot_to_index.load(slot))
            if key_index == 0:
                @parameter
                if caching_hashes:
                    self.key_hashes.store(slot, key_hash)
                self.values.append(value)
                self.count += 1
                self.slot_to_index.store(slot, SIMD[KeyCountType, 1](self.keys.count))
                return
            @parameter
            if caching_hashes:
                var other_key_hash = self.key_hashes[slot]
                if other_key_hash == key_hash:
                    var other_key = self.keys[key_index - 1]
                    if eq(other_key, key_ref):
                        self._add_next(value, key_index)
                        return
            else:
                var other_key = self.keys[key_index - 1]
                if eq(other_key, key_ref):
                    self._add_next(value, key_index)
                    return
            
            slot = (slot + 1) & modulo_mask

    @always_inline
    fn _add_next(inout self, value: V, key_index: Int):
        self.next_values.append(value)
        var next_index = self.next_values_index.get(key_index - 1)
        if not next_index:
            self.next_values_index[key_index - 1] = len(self.next_values) - 1
        else:
            var index = int(next_index.value())
            var next_next_index = self.next_next_values_index.get(index)
            while next_next_index:
                index = int(next_next_index.value())
                next_next_index = self.next_next_values_index.get(index)
            self.next_next_values_index[index] = len(self.next_values) - 1
        self.keys.drop_last()

    @always_inline
    fn _rehash(inout self) raises:
        var old_slot_to_index = self.slot_to_index
        var old_capacity = self.capacity
        self.capacity <<= 1
        self.slot_to_index = DTypePointer[KeyCountType].alloc(self.capacity)
        memset_zero(self.slot_to_index, self.capacity)
        
        var key_hashes = self.key_hashes
        @parameter
        if caching_hashes:
            key_hashes = DTypePointer[KeyCountType].alloc(self.capacity)
            
        var modulo_mask = self.capacity - 1
        for i in range(old_capacity):
            if old_slot_to_index[i] == 0:
                continue
            var key_hash = SIMD[KeyCountType, 1](0)
            @parameter
            if caching_hashes:
                key_hash = self.key_hashes[i]
            else:
                key_hash = hash(self.keys[int(old_slot_to_index[i] - 1)]).cast[KeyCountType]()

            var slot = int(key_hash & modulo_mask)

            while True:
                var key_index = int(self.slot_to_index.load(slot))

                if key_index == 0:
                    self.slot_to_index.store(slot, old_slot_to_index[i])
                    break
                else:
                    slot = (slot + 1) & modulo_mask
            @parameter
            if caching_hashes:
                key_hashes[slot] = key_hash  
        
        @parameter
        if caching_hashes:
            self.key_hashes.free()
            self.key_hashes = key_hashes
        old_slot_to_index.free()

    @always_inline
    fn get[T: Keyable](inout self, key: T) raises -> List[V]:
        var result = List[V]()
        self.key_builder.reset()
        key.accept(self.key_builder)
        var key_ref = self.key_builder.get_key()
        var key_index = self._find_key_index(key_ref)
        if key_index == 0:
            return result
        result.append(self.values[key_index - 1])
        var next_index = self.next_values_index.get(key_index - 1)
        if not next_index:
            return result
        var index = int(next_index.value())
        result.append(self.next_values[index])
        var next_next_index = self.next_next_values_index.get(index)
        while next_next_index:
            index = int(next_next_index.value())
            result.append(self.next_values[index])
            next_next_index = self.next_next_values_index.get(index)
        return result

    fn get_itter[T: Keyable](inout self, key: T) raises -> _ValuesIter[V, NextKeyCountType, __lifetime_of(self)]:
        self.key_builder.reset()
        key.accept(self.key_builder)
        var key_ref = self.key_builder.get_key()
        var key_index = self._find_key_index(key_ref)
        if key_index == 0:
            return _ValuesIter(None, None, self.values, self.next_values, self.next_next_values_index, True)
        var next_index = self.next_values_index.get(key_index - 1)
        if not next_index:
            return _ValuesIter(Optional(key_index - 1), None, self.values, self.next_values, self.next_next_values_index, True)
        return _ValuesIter(Optional(key_index - 1), Optional(int(next_index.value())), self.values, self.next_values, self.next_next_values_index, True)

    fn _find_key_index(self, key_ref: KeyRef) raises -> Int:
        var key_hash = hash(key_ref).cast[KeyCountType]()
        var modulo_mask = self.capacity - 1
        var slot = int(key_hash & modulo_mask)
        while True:
            var key_index = int(self.slot_to_index.load(slot))
            if key_index == 0:
                return key_index
            @parameter
            if caching_hashes:
                var other_key_hash = self.key_hashes[slot]
                if key_hash == other_key_hash:
                    var other_key = self.keys[key_index - 1]
                    if eq(other_key, key_ref):
                        return key_index
            else:
                var other_key = self.keys[key_index - 1]
                if eq(other_key, key_ref):
                    return key_index
            slot = (slot + 1) & modulo_mask

    fn debug(self) raises:
        print("Dict count:", self.count, "and capacity:", self.capacity)
        print("KeyMap:")
        for i in range(self.capacity):
            var end = ", " if i < self.capacity - 1 else ""
            print(self.slot_to_index.load(i), end=end)
        print("Keys:")
        self.keys.print_keys()
        @parameter
        if caching_hashes:
            print("KeyHashes:")
            for i in range(self.capacity):
                var end = ", " if i < self.capacity - 1 else ""
                if self.slot_to_index.load(i) > 0:
                    print(self.key_hashes.load(i), end=end)
                else:
                    print(0, end=end)
        print("Next Values:")
        self.next_values_index.debug()
        print("Next Next Values:")
        self.next_next_values_index.debug()

--- generic_dict/single_key_builder.mojo ---

from .keys_container import KeysBuilder, KeyRef

struct SingleKeyBuilder(KeysBuilder):
    var key: DTypePointer[DType.uint8]
    var allocated_bytes: Int
    var key_size: Int

    fn __init__(inout self, bytes: Int = 64):
        self.allocated_bytes = bytes
        self.key = DTypePointer[DType.uint8].alloc(self.allocated_bytes)
        self.key_size = 0

    fn __copyinit__(inout self, existing: Self):
        self.allocated_bytes = existing.allocated_bytes
        self.key = DTypePointer[DType.uint8].alloc(self.allocated_bytes)
        memcpy(self.key, existing.key, self.allocated_bytes)
        self.key_size = existing.key_size

    fn __moveinit__(inout self, owned existing: Self):
        self.allocated_bytes = existing.allocated_bytes
        self.key = existing.key
        self.key_size = existing.key_size

    fn __del__(owned self):
        self.key.free()

    @always_inline  
    fn add[T: DType, size: Int](inout self, value: SIMD[T, size]):
        var key_length = size * T.sizeof()
        var old_key_size = self.key_size
        self.key_size += key_length
        
        var needs_realocation = False
        while self.key_size > self.allocated_bytes:
            self.allocated_bytes += self.allocated_bytes >> 1
            needs_realocation = True

        if needs_realocation:
            var key = DTypePointer[DType.uint8].alloc(self.allocated_bytes)
            memcpy(key, self.key, old_key_size)
            self.key.free()
            self.key = key
        
        self.key.store(old_key_size, bitcast[DType.uint8, size * T.sizeof()](value))
    
    @always_inline
    fn add_buffer[T: DType](inout self, pointer: DTypePointer[T], size: Int):
        var key_length = size * T.sizeof()
        var old_key_size = self.key_size
        self.key_size += key_length
        
        var needs_realocation = False
        while self.key_size > self.allocated_bytes:
            self.allocated_bytes += self.allocated_bytes >> 1
            needs_realocation = True

        if needs_realocation:
            var key = DTypePointer[DType.uint8].alloc(self.allocated_bytes)
            memcpy(key, self.key, old_key_size)
            self.key.free()
            self.key = key
        
        memcpy(self.key.offset(old_key_size), pointer.bitcast[DType.uint8](), key_length)

    @always_inline
    fn get_key(self) -> KeyRef:
        return KeyRef(self.key, self.key_size)

    @always_inline
    fn reset(inout self):
        self.key_size = 0

--- generic_dict/sparse_array.mojo ---

from collections import Optional
from bit import pop_count
from tensor import Tensor, TensorSpec

struct SparseArray[T: DType]:
    var mask: DTypePointer[DType.uint8]
    var values: DTypePointer[T]
    var mask_size: Int
    var values_count: Int
    var values_capacity: Int

    fn __init__(inout self, capacity: Int = 8):
        var _capacity = capacity if capacity >= 8 else 8
        self.mask_size = -(-_capacity >> 3)
        self.mask = DTypePointer[DType.uint8].alloc(self.mask_size)
        memset_zero(self.mask, self.mask_size)
        self.values_capacity = 4
        self.values_count = 0
        self.values = DTypePointer[T].alloc(self.values_capacity)

    fn __copyinit__(inout self, existing: Self):
        self.mask_size = existing.mask_size
        self.values_count = existing.values_count
        self.values_capacity = existing.values_capacity
        self.mask = DTypePointer[DType.uint8].alloc(self.mask_size)
        memcpy(self.mask, existing.mask, self.mask_size)
        self.values = DTypePointer[T].alloc(self.values_capacity)
        memcpy(self.values, existing.values, self.values_count)

    fn __moveinit__(inout self, owned existing: Self):
        self.mask_size = existing.mask_size
        self.values_count = existing.values_count
        self.values_capacity = existing.values_capacity
        self.mask = existing.mask
        self.values = existing.values

    fn __del__(owned self):
        self.mask.free()
        self.values.free()

    @always_inline
    fn __contains__(self, index: Int) -> Bool:
        var offset = index >> 3
        var bit_index = index & 7
        return self.contains(offset, bit_index)

    @always_inline
    fn contains(self, offset: Int, bit_index: Int) -> Bool:
        return offset < self.mask_size and self.mask.load(offset) & (1 << bit_index) != 0

    fn __setitem__(inout self, index: Int, value: SIMD[T, 1]):
        var offset = index >> 3
        var bit_index = index & 7
        
        if self.mask_size <= offset:
            var mask = DTypePointer[DType.uint8].alloc(offset + 1)
            memcpy(mask, self.mask, self.mask_size)
            memset_zero(mask.offset(self.mask_size), offset + 1 - self.mask_size)
            self.mask.free()
            self.mask = mask
            self.mask_size = offset + 1
        
        var p = self.mask.offset(offset)
        var mask = p.load()

        if self.contains(offset, bit_index):
            self.values.store(self._value_index(offset, bit_index), value)
            return

        p.store(mask | (1 << bit_index))

        if self.values_capacity <= self.values_count + 1:
            var values_capacity = self.values_capacity + (self.values_capacity >> 1)
            var values = DTypePointer[T].alloc(values_capacity)
            memcpy(values, self.values, self.values_count)
            self.values.free()
            self.values = values
            self.values_capacity = values_capacity

        var value_index = self._value_index(offset, bit_index)
        for i in range(self.values_count, value_index, -1):
            self.values.store(i, self.values.load(i-1))
        self.values.store(value_index, value)
        self.values_count += 1

    fn get(self, index: Int) -> Optional[SIMD[T, 1]]:
        var offset = index >> 3
        var bit_index = index & 7
        if not self.contains(offset, bit_index):
            return None
        return self.values.load(self._value_index(offset, bit_index))

    @always_inline
    fn _value_index(self, offset: Int, bit_index: Int) -> Int:
        
        if not self.contains(offset, bit_index):
            return -1
        
        alias width = 32
        var cursor = 0
        var result = 0
        while cursor + width < offset:
            var v = self.mask.load[width=width](cursor)
            result += int(pop_count(v).cast[DType.int16]().reduce_add[1]())
            cursor += width
        
        while cursor <= offset:
            var v = self.mask.load(cursor)
            result += int(pop_count(v))
            cursor += 1

        result -= int(pop_count(self.mask.load(offset) >> (bit_index + 1)))
        return result - 1

    fn dense_values_list(self) -> List[Scalar[T]]:
        var data = UnsafePointer[Scalar[T]].alloc(self.values_count)
        memcpy(data, self.values, self.values_count)
        return List[Scalar[T]](unsafe_pointer=data, size=self.values_count, capacity=self.values_count)

    fn debug(self):
        print("(" + str(self.mask_size) + ")[")
        for i in range(self.mask_size):
            var end = ", " if i < self.mask_size - 1 else ""
            print(self.mask.load(i), end=end)
        print("]")

        print("(" + str(self.values_count) + ")[")
        for i in range(self.values_count):
            var end = ", " if i < self.mask_size - 1 else ""
            print(self.values.load(i), end=end)
        print("]")


--- memory_consumption_compact_dict.mojo ---

from string_dict import Dict
from corpora import system_words_collection, hindi_text_to_keys

fn main() raises:
    var corpus = system_words_collection()
    var dict = Dict[Int](len(corpus))
    for _ in range(100):
        for i in range(len(corpus)):
            dict.put(corpus[i], i)

    var sum = 0
    for _ in range(100):
        sum = 0
        for i in range(len(corpus)):
            sum += dict.get(corpus[i], -1)
    
    print(sum)


--- memory_consumption_std_lib_dict.mojo ---

from collections import Dict
from corpora import system_words_collection, hindi_text_to_keys

fn main() raises:
    var corpus = system_words_collection()
    var dict = Dict[String, Int]()
    for _ in range(100):
        for i in range(len(corpus)):
            dict[corpus[i]] =  i

    var sum = 0
    for _ in range(100):
        sum = 0
        for i in range(len(corpus)):
            sum += dict[corpus[i]]
    
    print(sum)


--- report_i7_2_8.csv ---

Corpus,Number of keys,Total bytes,Min key,Avg key,Max key,Build stdlib,Read stdlib,Delete stdlib,Read after delete stdlib,Build compact nc,Build compact,Read compact,Delete compact,Read after delete compact,Speedup build nc,Speedup build,Speedup read,Speadup delete,Speedup read after delete,Read Checksum,Read Checksum after delete
Arabic,463,4223,2,9.1209503239740819,26,282161.26750000002,266846.96649999998,25982.4938,251971.11499999999,44130.014150000003,37598.3321,33078.459300000002,3796.3750749999999,33273.6777,6.3938630642836527,7.5046219270987278,8.0670917614352113,6.844027074959131,7.5726860514730543,True,True
Chinese,10,4647,441,464.69999999999999,480,16025.14772063509,18314.451569610952,2830.3437250000002,16485.900081009782,6502.6824200000001,8495.0055049999992,4019.8425699999998,802.64647549999995,3991.0950549999998,2.4643903370318809,1.8864199335954508,4.5560121449261013,3.5262644406889945,4.1306708694788989,True,True
English,999,4289,1,4.293293293293293,13,516880.22249999997,502126.95250000001,39695.287499999999,410971.52649999998,77203.731549999997,73065.018899999995,65741.021649999995,8142.0887400000001,67550.941399999996,6.6950160584563099,7.0742501717193145,7.6379548096055485,4.8753199292691569,6.0838756349293455,True,True
French,471,2977,2,6.3205944798301488,19,341206.46399999998,246948.72200000001,21297.802361912833,242384.2065,43061.94915,37721.114300000001,33534.9614,3896.972655,33558.7333,7.9236186641588642,9.045503303172568,7.3639184806099109,5.4652172974800699,7.2226863968074753,True,True
Georgien,381,5982,6,15.700787401574804,42,259902.92249999999,224538.21063674512,21832.607244233364,213897.87081594602,39817.638099999996,34050.844400000002,28084.701550000002,3465.9476549999999,27987.880349999999,6.5273314767507511,7.6327893501519153,7.995036380820828,6.2991739684058681,7.6425176948402251,True,True
German,999,5644,2,5.6496496496496498,18,507176.1605,500103.71399999998,41164.0988,435041.69650000002,76674.384300000005,72348.200349999999,64904.020049999999,7966.6006150000003,66604.793550000002,6.6146753590559975,7.0102111461850631,7.7052810228817243,5.1670845306960329,6.5316874854272422,True,True
Greek,452,4693,3,10.382743362831858,28,268406.69199999998,256531.18599999999,27333.841799999998,244574.5575,43754.644950000002,37785.7929,31911.987150000001,3830.38267,32000.785650000002,6.1343588162289509,7.1033759357739994,8.0387092409568108,7.1360603247507921,7.642767279996451,True,True
Hebrew,376,3346,2,8.8989361702127656,25,220431.24414348463,192329.32168501677,16003.335905794691,180609.85216659011,34367.662700000001,30125.461050000002,25570.2399,3110.0909099999999,26677.436600000001,6.4139143260238232,7.3171077374593274,7.5216080270336745,5.1456167581270762,6.7701351848246967,True,True
Japanese,10,4992,378,499.19999999999999,558,18134.214944105184,18792.600778070293,2486.7059549999999,16540.184582894799,7786.0123899999999,8632.5784600000006,4200.9365500000004,872.44017699999995,4230.3348150000002,2.3290760450620329,2.1006718940502034,4.4734312347731828,2.8502882152342695,3.9098996429895587,True,True
l33t,487,2317,2,4.7577002053388089,14,284474.48849999998,267964.25300000003,33560.018600000003,268617.7745,42140.019650000002,37495.18535,32642.908800000001,3984.9862750000002,32958.36505,6.750696626692247,7.5869604549107805,8.2089575607918857,8.4216146014204298,8.1502154033578194,True,True
S3,161,3582,8,22.248447204968944,43,109559.598,99783.478099999993,8019.4958800000004,96937.528099999996,19218.501141948658,16048.145968523408,12074.652835913561,1462.8362449864971,12026.096542032623,5.7007358269403117,6.8269317972860257,8.2638796705785733,5.4821555778952042,8.0605978640859863,True,True
Words,104335,880750,1,8.4416393505472804,23,82684071.5,55425389.399999999,4880371.8799999999,54811311.600000001,10383361.395,9020746.9649999999,8160072.2599999998,1036582.8945000001,8245112.3300000001,7.9631314325451168,9.1659894486354219,6.7922669841651633,4.7081346855082611,6.6477337610753935,True,True



--- report_m1.csv ---

Corpus,Number of keys,Total bytes,Min key,Avg key,Max key,Build stdlib,Read stdlib,Delete stdlib,Read after delete stdlib,Build compact nc,Build compact,Read compact,Delete compact,Read after delete compact,Speedup build nc,Speedup build,Speedup read,Speadup delete,Speedup read after delete,Read Checksum,Read Checksum after delete
Arabic,463,4223,2,9.1209503239740819,26,235932.5,223595.52401746725,22670.877683403218,218722.66157622033,37214.300000000003,34868.900000000001,28109.849999999999,3218.5900000000001,31558.549999999999,6.3398344184896658,6.7662730972299094,7.9543478182013514,7.0437296093641057,6.9306942675192724,True,True
Chinese,10,4647,441,464.69999999999999,480,15724.874561912206,18064.8962184507,4231.6149999999998,19025.38622655475,5201.1899999999996,7042.125,3814.7350000000001,638.20950000000005,3478.0749999999998,3.0233224631117506,2.2329729395476798,4.7355573109143103,6.6304481522133409,5.4700908481141877,True,True
English,999,4289,1,4.293293293293293,13,427551.5,420086.5,23105.549999999999,286848.5,62043.0,53854.050000000003,49204.099999999999,5579.9350000000004,48462.550000000003,6.8912125461373561,7.9390779337858524,8.5376320265994092,4.1408278053418179,5.9189724849394016,True,True
French,471,2977,2,6.3205944798301488,19,283979.5,241187.0,12612.260450748878,191136.0681114551,39083.949999999997,27904.549999999999,25878.400000000001,2977.4000000000001,30242.049999999999,7.2658853570327464,10.176817042381979,9.3200120563867941,4.2359980018636652,6.3202087196950973,True,True
Georgien,381,5982,6,15.700787401574804,42,244965.5,197522.29097255276,15711.83870835553,182804.96546900953,36506.699999999997,29836.299999999999,26632.0,2996.605,25659.75,6.7101518351425895,8.2103176332185956,7.4167276574253815,5.2432131389874632,7.124191212658328,True,True
German,999,5644,2,5.6496496496496498,18,460852.5,419771.5,23377.549999999999,344666.5,61785.449999999997,60368.050000000003,47545.400000000001,5651.8900000000003,52190.25,7.4589162982546862,7.6340464865106634,8.8288562090128586,4.1362358432312023,6.6040400266333261,True,True
Greek,452,4693,3,10.382743362831858,28,244522.5,221500.94654286487,24229.25,214683.0,38830.349999999999,31879.25,30672.950000000001,3340.5599999999999,28890.849999999999,6.2972005145459669,7.6702714147917552,7.2213773550592588,7.253050386761501,7.4308301763361069,True,True
Hebrew,376,3346,2,8.8989361702127656,25,233578.5,168860.83752093802,12954.055595892542,151668.27673329864,29366.349999999999,23654.950000000001,21462.204182949532,2668.6300000000001,21630.732621223018,7.9539506952685644,9.874402609179052,7.867823643905508,4.8541969459582415,7.0117031812639183,True,True
Hindi,450,8280,9,18.399999999999999,51,279719.5,261702.0,16873.475787181891,210054.84571925251,48077.199999999997,36368.800000000003,34604.75,3576.3200000000002,31252.849999999999,5.8181320875591753,7.6911941004377375,7.5626033998222786,4.7181112951810498,6.7211420948570302,True,True
Japanese,10,4992,378,499.19999999999999,558,16569.322489095444,20352.976730129947,4614.2399999999998,24709.549999999999,6137.2650000000003,8562.8150000000005,3843.0749999999998,697.17399999999998,3811.1550000000002,2.6997893180586869,1.935032169805776,5.2960134085673447,6.6184912231379833,6.4834807295950965,True,True
l33t,487,2317,2,4.7577002053388089,14,276193.0,237513.5,24396.650000000001,243392.5,34135.050000000003,29055.049999999999,27157.200000000001,2879.3049999999998,26537.799999999999,8.0911848671673265,9.5058518226607784,8.7458758634910811,8.4731037524680453,9.1715402181039885,True,True
Russian,999,10636,2,10.646646646646646,37,604495.0,475855.5,39374.800000000003,414928.0,84918.649999999994,73036.600000000006,61565.75,7363.0749999999998,64405.650000000001,7.1185187235077345,8.2766037849516554,7.7292244470342686,5.3476027339121224,6.4424161544833405,True,True
S3,161,3582,8,22.248447204968944,43,95685.949999999997,86654.449999999997,7334.6599999999999,79889.0,29397.849999999999,15670.69554658157,12936.078249315638,1437.7824488910567,12447.492445545658,3.2548621752951323,6.1060435840624239,6.6986646439452624,5.1013698252173887,6.4180798140261839,True,True
Words,235977,2257909,1,9.5683840729565723,28,159431769.23076922,114977950.0,10390735.0,111888000.0,21706760.683760684,17649079.207920793,14882849.056603774,2961165.0,15481181.25,7.3447978513921583,9.0334326993794267,7.7255335697288627,3.5090023690000387,7.2273554706944614,True,True



--- report_m1_new.csv ---

Corpus,Number of keys,Total bytes,Min key,Avg key,Max key,Build stdlib,Read stdlib,Delete stdlib,Read after delete stdlib,Build compact nc,Build compact,Read compact,Delete compact,Read after delete compact,Speedup build nc,Speedup build,Speedup read,Speadup delete,Speedup read after delete,Read Checksum,Read Checksum after delete
Arabic,463,4223,2,9.1209503239740819,26,77512.350000000006,61041.650000000001,6077.0900000000001,60665.75,36217.150000000001,35177.099999999999,29045.450000000001,3385.3800000000001,36741.949999999997,2.1402111982858947,2.2034889175059913,2.1015907827215625,1.7950983346035012,1.6511303836622715,True,True
Chinese,10,4647,441,464.69999999999999,480,5740.8000000000002,8005.4849999999997,1132.5329999999999,7623.1450000000004,5382.9949999999999,5126.1850000000004,4430.7849999999999,869.87894017913936,4922.875,1.0664695025724527,1.1198971554869752,1.8067870591780013,1.3019432333501151,1.5485148414290431,True,True
English,999,4289,1,4.293293293293293,13,121901.95,105672.3,12042.870000000001,103702.10000000001,65044.800000000003,50951.150000000001,48228.25,6271.0649999999996,58321.449999999997,1.874122912208201,2.3925259783145227,2.1910871740110829,1.9203867285700278,1.7781125126347168,True,True
French,471,2977,2,6.3205944798301488,19,81442.050000000003,54487.150000000001,5469.7650000000003,52006.400000000001,34522.300000000003,27519.099999999999,30547.400000000001,3276.1149999999998,29836.650000000001,2.3591142536852989,2.9594736019709944,1.7836919017657797,1.6695888270100407,1.7430375058862171,True,True
Georgien,381,5982,6,15.700787401574804,42,63962.949999999997,52573.150000000001,5155.1049999999996,52648.5,36022.5,28743.5,31056.650000000001,3157.625,29115.849999999999,1.7756388368380871,2.2253013724842137,1.6928145823841272,1.6325893670084317,1.8082419026063121,True,True
German,999,5644,2,5.6496496496496498,18,124514.95,103500.25,12231.155000000001,104708.25,66339.25,53060.900000000001,52700.650000000001,5990.3900000000003,62306.199999999997,1.8769423832798831,2.3466422544660945,1.9639273898898779,2.0417961101030153,1.6805430278206663,True,True
Greek,452,4693,3,10.382743362831858,28,72400.350000000006,54366.199999999997,6382.6350000000002,58440.0,38861.449999999997,31355.0,34619.099999999999,3638.8899999999999,32193.349999999999,1.8630377919506353,2.3090527826502951,1.570410553711679,1.7540060293111359,1.8152817274374988,True,True
Hebrew,376,3346,2,8.8989361702127656,25,63110.25,47014.050000000003,4731.3149999999996,43050.599999999999,30170.349999999999,23344.54666629382,22286.694296287751,2655.5349999999999,23653.25,2.0917970789201981,2.7034258108392466,2.1095120422516427,1.7816805276526197,1.8200712375677761,True,True
Hindi,450,8280,9,18.399999999999999,51,72564.25,59076.199999999997,5954.8900000000003,59433.400000000001,43256.699999999997,35778.050000000003,32692.049999999999,3832.1550000000002,37412.449999999997,1.6775262560481961,2.0281778911930637,1.807050949695721,1.5539272289351556,1.5885995170057026,True,True
Japanese,10,4992,378,499.19999999999999,558,6018.9499999999998,7916.6400000000003,1192.5155,7461.915,5876.1800000000003,4899.4099999999999,4751.3050000000003,956.60199999999998,4724.46,1.0242963966386325,1.2285050648955689,1.6662032852026971,1.2466161475723447,1.5794217751870054,True,True
l33t,487,2317,2,4.7577002053388089,14,67924.800000000003,53437.599999999999,6322.6049999999996,54617.75,35199.150000000001,28267.950000000001,26417.549999999999,3097.415,30473.0,1.9297284167373356,2.4028909064859674,2.0228068083527808,2.0412521408981359,1.7923325566895283,True,True
Russian,999,10636,2,10.646646646646646,37,155766.14978969176,118214.14999999999,13390.757523271017,119861.84210526316,81256.649999999994,70636.649999999994,67257.149999999994,7817.6949999999997,71723.449999999997,1.9169649473574382,2.2051746478590331,1.7576443545407441,1.7128779676453247,1.6711667119367957,True,True
S3,161,3582,8,22.248447204968944,43,27222.849999999999,20800.466086722419,2110.5573605021605,24579.299999999999,18197.410576168797,13987.306483529275,13269.252762453505,1511.4813406196954,13797.569452179918,1.4959738302355423,1.9462539147230535,1.5675687590773106,1.3963502583742444,1.7814224516272787,True,True
Words,235977,2257909,1,9.5683840729565723,28,65289550.0,32252450.0,5270680.0,35955200.0,22515927.927927926,19531765.151515152,16623086.330935251,2298315.0,18200452.05479452,2.899705053639706,3.3427367927847138,1.940220327195125,2.2932800769259218,1.9755113714622474,True,True



--- string_dict/__init__.mojo ---

from .dict import Dict

--- string_dict/ahasher.mojo ---

# This code is based on https://github.com/tkaitchuck/aHash

from bit import rotate_bits_left, byte_swap

alias U256 = SIMD[DType.uint64, 4]
alias U128 = SIMD[DType.uint64, 2]
alias MULTIPLE = 6364136223846793005
alias ROT = 23


@always_inline
fn folded_multiply(s: UInt64, by: UInt64) -> UInt64:
    var b1 = s * byte_swap(by)
    var b2 = byte_swap(s) * (~by)
    return b1 ^ byte_swap(b2)


@always_inline
fn read_small(data: DTypePointer[DType.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[DType.uint64]()
            return U128(a, b)
        else:
            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:
        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):
        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 update(inout self, new_data: UInt64):
        self.buffer = folded_multiply(new_data ^ self.buffer, MULTIPLE)

    @always_inline
    fn large_update(inout self, new_data: U128):
        var combined = folded_multiply(
            new_data[0] ^ self.extra_keys[0], new_data[1] ^ self.extra_keys[1]
        )
        self.buffer = rotate_bits_left[ROT]((self.buffer + self.pad) ^ combined)
    
    @always_inline
    fn short_finish(self) -> UInt64:
        return self.buffer + self.pad

    @always_inline
    fn finish(self) -> UInt64:
        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: DTypePointer[DType.uint8], length: Int):
        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)

@always_inline
fn ahash(s: String) -> UInt64:
    var length = len(s)
    var b = s.unsafe_ptr()
    var hasher = AHasher(U256(0, 0, 0, 0))

    if length > 8:
        hasher.write(b, length)
    else:
        var value = read_small(b, length)
        hasher.buffer = folded_multiply(value[0] ^ hasher.buffer, value[1] ^ hasher.extra_keys[1])
        hasher.pad = hasher.pad + length
    
    return hasher.finish()


--- string_dict/dict.mojo ---

from bit import pop_count, bit_width
from memory import memset_zero, memcpy
from collections import List
from .string_eq import eq
from .keys_container import KeysContainer
from .ahasher import ahash

struct Dict[
    V: CollectionElement, 
    hash: fn(String) -> UInt64 = ahash,
    KeyCountType: DType = DType.uint32,
    KeyOffsetType: DType = DType.uint32,
    destructive: Bool = True,
    caching_hashes: Bool = True,
](Sized):
    var keys: KeysContainer[KeyOffsetType]
    var key_hashes: DTypePointer[KeyCountType]
    var values: List[V]
    var slot_to_index: DTypePointer[KeyCountType]
    var deleted_mask: DTypePointer[DType.uint8]
    var count: Int
    var capacity: Int

    fn __init__(inout self, capacity: Int = 16):
        constrained[
            KeyCountType == DType.uint8 or 
            KeyCountType == DType.uint16 or 
            KeyCountType == DType.uint32 or 
            KeyCountType == DType.uint64,
            "KeyCountType needs to be an unsigned integer"
        ]()
        self.count = 0
        if capacity <= 8:
            self.capacity = 8
        else:
            var icapacity = Int64(capacity)
            self.capacity = capacity if pop_count(icapacity) == 1 else
                            1 << int(bit_width(icapacity))
        self.keys = KeysContainer[KeyOffsetType](capacity)
        @parameter
        if caching_hashes:
            self.key_hashes = DTypePointer[KeyCountType].alloc(self.capacity)
        else:
            self.key_hashes = DTypePointer[KeyCountType].alloc(0)
        self.values = List[V](capacity=capacity)
        self.slot_to_index = DTypePointer[KeyCountType].alloc(self.capacity)
        memset_zero(self.slot_to_index, self.capacity)
        @parameter
        if destructive:
            self.deleted_mask = DTypePointer[DType.uint8].alloc(self.capacity >> 3)
            memset_zero(self.deleted_mask, self.capacity >> 3)
        else:
            self.deleted_mask = DTypePointer[DType.uint8].alloc(0)

    fn __copyinit__(inout self, existing: Self):
        self.count = existing.count
        self.capacity = existing.capacity
        self.keys = existing.keys
        @parameter
        if caching_hashes:
            self.key_hashes = DTypePointer[KeyCountType].alloc(self.capacity)
            memcpy(self.key_hashes, existing.key_hashes, self.capacity)
        else:
            self.key_hashes = DTypePointer[KeyCountType].alloc(0)
        self.values = existing.values
        self.slot_to_index = DTypePointer[KeyCountType].alloc(self.capacity)
        memcpy(self.slot_to_index, existing.slot_to_index, self.capacity)
        @parameter
        if destructive:
            self.deleted_mask = DTypePointer[DType.uint8].alloc(self.capacity >> 3)
            memcpy(self.deleted_mask, existing.deleted_mask, self.capacity >> 3)
        else:
            self.deleted_mask = DTypePointer[DType.uint8].alloc(0)

    fn __moveinit__(inout self, owned existing: Self):
        self.count = existing.count
        self.capacity = existing.capacity
        self.keys = existing.keys^
        self.key_hashes = existing.key_hashes
        self.values = existing.values^
        self.slot_to_index = existing.slot_to_index
        self.deleted_mask = existing.deleted_mask

    fn __del__(owned self):
        self.slot_to_index.free()
        self.deleted_mask.free()
        self.key_hashes.free()

    fn __len__(self) -> Int:
        return self.count

    @always_inline
    fn __contains__(inout self, key: String) -> Bool:
        return self._find_key_index(key) != 0

    fn put(inout self, key: String, value: V):
        if self.count / self.capacity >= 0.87:
            self._rehash()
        
        var key_hash = hash(key).cast[KeyCountType]()
        var modulo_mask = self.capacity - 1
        var slot = int(key_hash & modulo_mask)
        while True:
            var key_index = int(self.slot_to_index.load(slot))
            if key_index == 0:
                self.keys.add(key)
                @parameter
                if caching_hashes:
                    self.key_hashes.store(slot, key_hash)
                self.values.append(value)
                self.count += 1
                self.slot_to_index.store(slot, SIMD[KeyCountType, 1](self.keys.count))
                return
            @parameter
            if caching_hashes:
                var other_key_hash = self.key_hashes[slot]
                if other_key_hash == key_hash:
                    var other_key = self.keys[key_index - 1]
                    if eq(other_key, key):
                        self.values[key_index - 1] = value # replace value
                        @parameter
                        if destructive:
                            if self._is_deleted(key_index - 1):
                                self.count += 1
                                self._not_deleted(key_index - 1)
                        return
            else:
                var other_key = self.keys[key_index - 1]
                if eq(other_key, key):
                    self.values[key_index - 1] = value # replace value
                    @parameter
                    if destructive:
                        if self._is_deleted(key_index - 1):
                            self.count += 1
                            self._not_deleted(key_index - 1)
                    return
            
            slot = (slot + 1) & modulo_mask

    @always_inline
    fn _is_deleted(self, index: Int) -> Bool:
        var offset = index >> 3
        var bit_index = index & 7
        return self.deleted_mask.offset(offset).load() & (1 << bit_index) != 0

    @always_inline
    fn _deleted(self, index: Int):
        var offset = index >> 3
        var bit_index = index & 7
        var p = self.deleted_mask.offset(offset)
        var mask = p.load()
        p.store(mask | (1 << bit_index))
    
    @always_inline
    fn _not_deleted(self, index: Int):
        var offset = index >> 3
        var bit_index = index & 7
        var p = self.deleted_mask.offset(offset)
        var mask = p.load()
        p.store(mask & ~(1 << bit_index))

    @always_inline
    fn _rehash(inout self):
        var old_slot_to_index = self.slot_to_index
        var old_capacity = self.capacity
        self.capacity <<= 1
        var mask_capacity = self.capacity >> 3
        self.slot_to_index = DTypePointer[KeyCountType].alloc(self.capacity)
        memset_zero(self.slot_to_index, self.capacity)
        
        var key_hashes = self.key_hashes
        @parameter
        if caching_hashes:
            key_hashes = DTypePointer[KeyCountType].alloc(self.capacity)
            
        @parameter
        if destructive:
            var deleted_mask = DTypePointer[DType.uint8].alloc(mask_capacity)
            memset_zero(deleted_mask, mask_capacity)
            memcpy(deleted_mask, self.deleted_mask, old_capacity >> 3)
            self.deleted_mask.free()
            self.deleted_mask = deleted_mask

        var modulo_mask = self.capacity - 1
        for i in range(old_capacity):
            if old_slot_to_index[i] == 0:
                continue
            var key_hash = SIMD[KeyCountType, 1](0)
            @parameter
            if caching_hashes:
                key_hash = self.key_hashes[i]
            else:
                key_hash = hash(self.keys[int(old_slot_to_index[i] - 1)]).cast[KeyCountType]()

            var slot = int(key_hash & modulo_mask)

            # var searching = True
            while True:
                var key_index = int(self.slot_to_index.load(slot))

                if key_index == 0:
                    self.slot_to_index.store(slot, old_slot_to_index[i])
                    break
                    # searching = False

                else:
                    slot = (slot + 1) & modulo_mask
            @parameter
            if caching_hashes:
                key_hashes[slot] = key_hash  
        
        @parameter
        if caching_hashes:
            self.key_hashes.free()
            self.key_hashes = key_hashes
        old_slot_to_index.free()

    fn get(self, key: String, default: V) -> V:
        var key_index = self._find_key_index(key)
        if key_index == 0:
            return default

        @parameter
        if destructive: 
            if self._is_deleted(key_index - 1):
                return default
        return self.values[key_index - 1]

    fn delete(inout self, key: String):
        @parameter
        if not destructive:
            return

        var key_index = self._find_key_index(key)
        if key_index == 0:
                return
        if not self._is_deleted(key_index - 1):
            self.count -= 1
        self._deleted(key_index - 1)

    fn upsert(inout self, key: String, update: fn(value: Optional[V]) -> V):
        var key_index = self._find_key_index(key)
        if key_index == 0:
            var value = update(None)
            self.put(key, value)
        else:
            key_index -= 1

            @parameter
            if destructive: 
                if self._is_deleted(key_index):
                    self.values[key_index] = update(None)
                    return
            
            self.values[key_index] = update(self.values[key_index])

    fn clear(inout self):
        self.values.clear()
        self.keys.clear() 
        memset_zero(self.slot_to_index, self.capacity)
        @parameter
        if destructive:
            memset_zero(self.deleted_mask, self.capacity >> 3)
        self.count = 0

    @always_inline
    fn _find_key_index(self, key: String) -> Int:
        var key_hash = hash(key).cast[KeyCountType]()
        var modulo_mask = self.capacity - 1

        var slot = int(key_hash & modulo_mask)
        while True:
            var key_index = int(self.slot_to_index.load(slot))
            if key_index == 0:
                return key_index
            
            @parameter
            if caching_hashes:
                var other_key_hash = self.key_hashes[slot]
                if key_hash == other_key_hash:
                    var other_key = self.keys[key_index - 1]
                    if eq(other_key, key):
                        return key_index
            else:
                var other_key = self.keys[key_index - 1]
                if eq(other_key, key):
                    return key_index
            
            slot = (slot + 1) & modulo_mask

    fn debug(self):
        print("Dict count:", self.count, "and capacity:", self.capacity)
        print("KeyMap:")
        for i in range(self.capacity):
            var end = ", " if i < self.capacity - 1 else "\n"
            print(self.slot_to_index.load(i), end=end)
        print("Keys:")
        self.keys.print_keys()
        @parameter
        if caching_hashes:
            print("KeyHashes:")
            for i in range(self.capacity):
                var end = ", " if i < self.capacity - 1 else "\n"
                if self.slot_to_index.load(i) > 0:
                    print(self.key_hashes.load(i), end=end)
                else:
                    print(0, end=end)


--- string_dict/keys_container.mojo ---

from collections.vector import InlinedFixedVector

struct KeysContainer[KeyEndType: DType = DType.uint32](Sized):
    var keys: DTypePointer[DType.uint8]
    var allocated_bytes: Int
    var keys_end: DTypePointer[KeyEndType]
    var count: Int
    var capacity: Int

    fn __init__(inout self, capacity: Int):
        constrained[
            KeyEndType == DType.uint8 or 
            KeyEndType == DType.uint16 or 
            KeyEndType == DType.uint32 or 
            KeyEndType == DType.uint64,
            "KeyEndType needs to be an unsigned integer"
        ]()
        self.allocated_bytes = capacity << 3
        self.keys = DTypePointer[DType.uint8].alloc(self.allocated_bytes)
        self.keys_end = DTypePointer[KeyEndType].alloc(capacity)
        self.count = 0
        self.capacity = capacity

    fn __copyinit__(inout self, existing: Self):
        self.allocated_bytes = existing.allocated_bytes
        self.count = existing.count
        self.capacity = existing.capacity
        self.keys = DTypePointer[DType.uint8].alloc(self.allocated_bytes)
        memcpy(self.keys, existing.keys, self.allocated_bytes)
        self.keys_end = DTypePointer[KeyEndType].alloc(self.allocated_bytes)
        memcpy(self.keys_end, existing.keys_end, self.capacity)

    fn __moveinit__(inout self, owned existing: Self):
        self.allocated_bytes = existing.allocated_bytes
        self.count = existing.count
        self.capacity = existing.capacity
        self.keys = existing.keys
        self.keys_end = existing.keys_end

    fn __del__(owned self):
        self.keys.free()
        self.keys_end.free()

    @always_inline
    fn add(inout self, key: String):
        var prev_end = 0 if self.count == 0 else self.keys_end[self.count - 1]
        var key_length = len(key)
        var new_end = prev_end + key_length
        
        var needs_realocation = False
        while new_end > self.allocated_bytes:
            self.allocated_bytes += self.allocated_bytes >> 1
            needs_realocation = True

        if needs_realocation:
            var keys = DTypePointer[DType.uint8].alloc(self.allocated_bytes)
            memcpy(keys, self.keys, int(prev_end))
            self.keys.free()
            self.keys = keys
        
        memcpy(self.keys.offset(prev_end), DTypePointer(key.unsafe_ptr()), key_length)
        var count = self.count + 1
        if count >= self.capacity:
            var new_capacity = self.capacity + (self.capacity >> 1)
            var keys_end = DTypePointer[KeyEndType].alloc(self.allocated_bytes)
            memcpy(keys_end, self.keys_end, self.capacity)
            self.keys_end.free()
            self.keys_end = keys_end
            self.capacity = new_capacity

        self.keys_end.store(self.count, new_end)
        self.count = count


    @always_inline
    fn get(self, index: Int) -> StringRef:
        if index < 0 or index >= self.count:
            return ""
        var start = 0 if index == 0 else int(self.keys_end[index - 1])
        var length = int(self.keys_end[index]) - start
        return StringRef(self.keys.offset(start), length)

    @always_inline
    fn clear(inout self):
        self.count = 0

    @always_inline
    fn __getitem__(self, index: Int) -> StringRef:
        return self.get(index)

    @always_inline
    fn __len__(self) -> Int:
        return self.count

    fn keys_vec(self) -> InlinedFixedVector[StringRef]:
        var keys = InlinedFixedVector[StringRef](self.count)
        for i in range(self.count):
            keys.append(self[i])
        return keys

    fn print_keys(self):
        print("(" + str(self.count) + ")[", end="")
        for i in range(self.count):
            var end = ", " if i < self.capacity - 1 else ""
            print(self[i], end=end)
        print("]")


--- string_dict/string_eq.mojo ---

@always_inline
fn eq(a: StringRef, b: String) -> Bool:
    var l = len(a)
    if l != len(b):
        return False
    var p1 = DTypePointer(a.data)
    var p2 = DTypePointer(b.unsafe_ptr())
    var offset = 0
    alias step = 16
    while l - offset >= step and (p1.load[width=step](offset) == p2.load[width=step](offset)).reduce_and():
        offset += step
    if l - offset >= step:
        return False
    while l - offset > 0 and p1.load(offset) == p2.load(offset):
        offset += 1
    return l - offset == 0


--- test_generic_dict.mojo ---

from generic_dict import Dict, Keyable, KeysBuilder
from testing import assert_equal

from corpora import *

@value
struct Person(Keyable):
    var name: String
    var age: Int

    fn accept[T: KeysBuilder](self, inout keys_builder: T):
        keys_builder.add_buffer[DType.uint8](self.name.unsafe_ptr(), len(self.name))
        keys_builder.add(Int64(self.age))

fn test_person_dict() raises:
    var p1 = Person("Maxim", 42)
    var p2 = Person("Maximilian", 62)
    var p3 = Person("Alex", 25)
    var p4 = Person("Maria", 28)
    var p5 = Person("Daria", 13)
    var p6 = Person("Max", 31)

    var d = Dict[Int]()
    _= d.put(p1, 1)
    _= d.put(p2, 11)
    _= d.put(p3, 111)
    _= d.put(p4, 1111)
    _= d.put(p5, 11111)
    _= d.put(p6, 111111)

    assert_equal(d.get(p1, 0), 1)
    # assert_equal(d.get(p2, 0), 11)
    # assert_equal(d.get(p3, 0), 111)
    # assert_equal(d.get(p4, 0), 1111)
    # assert_equal(d.get(p5, 0), 11111)
    # assert_equal(d.get(p6, 0), 111111)

@value
struct StringKey(Keyable):
    var s: String

    fn __init__(inout self, owned s: String):
        self.s = s^

    fn __init__(inout self, s: StringLiteral):
        self.s = String(s)

    fn accept[T: KeysBuilder](self, inout keys_builder: T):
        alias type_prefix = "String:"
        keys_builder.add_buffer(type_prefix.unsafe_ptr(), len(type_prefix))
        keys_builder.add_buffer(self.s.unsafe_ptr(), len(self.s))

@value 
struct IntKey(Keyable):
    var i: Int

    fn __init__(inout self, i: Int):
        self.i = i

    fn accept[T: KeysBuilder](self, inout keys_builder: T):
        alias type_prefix = "Int:"
        keys_builder.add_buffer(type_prefix.unsafe_ptr(), len(type_prefix))
        keys_builder.add(Int64(self.i))

fn test_add_vs_update() raises:
    var d = Dict[Int]()
    assert_equal(d.put(StringKey("a"), 1), True)
    assert_equal(d.put(StringKey("a"), 2), False)
    d.delete(StringKey("a"))
    assert_equal(d.put(StringKey("a"), 3), True)
    assert_equal(d.put(StringKey("a"), 4), False)
    assert_equal(d.get(StringKey("a"), 0), 4)

fn test_clear() raises:
    var d = Dict[Int]()
    assert_equal(d.put(StringKey("a"), 1), True)
    assert_equal(d.put(StringKey("b"), 1), True)
    assert_equal(d.put(StringKey("a"), 2), False)
    assert_equal(d.get(StringKey("a"), 0), 2)
    d.clear()
    assert_equal(d.put(StringKey("a"), 3), True)
    assert_equal(d.get(StringKey("a"), 0), 3)
    assert_equal(d.get(StringKey("b"), 0), 0)

fn test_no_key_collision() raises:
    var d = Dict[Int]()
    assert_equal(d.put(StringKey("a"), 1), True)
    assert_equal(d.put(IntKey(97), 2), True)
    assert_equal(d.get(StringKey("a"), 0), 1)
    assert_equal(d.get(IntKey(97), 0), 2)


fn main() raises:
    test_person_dict()
    test_add_vs_update()
    test_clear()
    test_no_key_collision()


--- test_multi_dict.mojo ---

from generic_dict import MultiDict, Keyable, KeysBuilder
from testing import assert_equal

from corpora import *

@value
struct StringKey(Keyable):
    var s: String

    fn __init__(inout self, owned s: String):
        self.s = s^

    fn __init__(inout self, s: StringLiteral):
        self.s = String(s)

    fn accept[T: KeysBuilder](self, inout keys_builder: T):
        keys_builder.add_buffer(self.s.unsafe_ptr(), len(self.s))

fn test_add() raises:
    var d = MultiDict[Int]()
    d.put(StringKey("a"), 1)
    d.put(StringKey("b"), 2)
    d.put(StringKey("c"), 3)
    d.put(StringKey("a"), 4)
    d.put(StringKey("a"), 5)
    d.put(StringKey("a"), 6)
    d.put(StringKey("c"), 7)

    assert_equal(len(d.get(StringKey("a"))), 4)
    assert_equal(d.get(StringKey("a"))[0], 1)
    assert_equal(d.get(StringKey("a"))[1], 4)
    assert_equal(d.get(StringKey("a"))[2], 5)
    assert_equal(d.get(StringKey("a"))[3], 6)
    assert_equal(len(d.get(StringKey("b"))), 1)
    assert_equal(d.get(StringKey("b"))[0], 2)
    assert_equal(len(d.get(StringKey("c"))), 2)
    assert_equal(d.get(StringKey("c"))[0], 3)
    assert_equal(d.get(StringKey("c"))[1], 7)

fn test_s3_corpus() raises:
    var d = MultiDict[
        Int, 
        KeyCountType=DType.uint8, 
        KeyOffsetType=DType.uint16, 
        NextKeyCountType=DType.uint8
    ]()
    var corpus = s3_action_names()
    for i in range(len(corpus)):
        d.put(StringKey(corpus[i]), i)
    
    assert_equal(len(d), 143)

    var all_values = 0
    for i in range(len(corpus)):
        var v = d.get(StringKey(corpus[i]))
        var c = len(v)
        all_values += c

    assert_equal(all_values, 143 + (len(corpus) - 143) * 3)
    _ = d

fn test_system_corpus() raises:
    var d = MultiDict[Int]()
    var corpus = system_words_collection()
    for i in range(len(corpus)):
        d.put(StringKey(corpus[i]), i)
    
    assert_equal(len(d), len(corpus))

    var all_values = 0
    for i in range(len(corpus)):
        var v = d.get(StringKey(corpus[i]))
        var c = len(v)
        all_values += c

    assert_equal(all_values, len(corpus))
    _ = d

fn test_english_corpus() raises:
    var d = MultiDict[
        Int, 
        KeyCountType=DType.uint16, 
        KeyOffsetType=DType.uint16, 
        NextKeyCountType=DType.uint16
    ]()
    var corpus = english_text_to_keys()
    for i in range(len(corpus)):
        d.put(StringKey(corpus[i]), i)
    assert_equal(len(d), 192)

    var all_values = 0
    for i in range(len(corpus)):
        var v = d.get(StringKey(corpus[i]))
        var c = len(v)
        all_values += c

    assert_equal(all_values, 18631)

    var the_occurances = 0
    for i in range(len(corpus)):
        if corpus[i] == "the":
            the_occurances += 1
    assert_equal(len(d.get(StringKey("the"))), the_occurances)
    _ = d

fn test_get_itter() raises:
    var d = MultiDict[Int]()
    d.put(StringKey("a"), 1)
    d.put(StringKey("b"), 2)
    d.put(StringKey("c"), 3)
    d.put(StringKey("a"), 4)
    d.put(StringKey("a"), 5)
    d.put(StringKey("a"), 6)
    d.put(StringKey("c"), 7)

    var index_a = 0
    var expected_a = List[Int](1, 4, 5, 6)
    for v in d.get_itter(StringKey("a")):
        assert_equal(expected_a[index_a], v[])
        index_a += 1

    assert_equal(index_a, 4)

    var index_b = 0
    var expected_b = List[Int](2)
    for v in d.get_itter(StringKey("b")):
        assert_equal(expected_b[index_b], v[])
        index_b += 1
    assert_equal(index_b, 1)

    var index_c = 0
    var expected_c = List[Int](3, 7)
    for v in d.get_itter(StringKey("c")):
        assert_equal(expected_c[index_c], v[])
        index_c += 1
    assert_equal(index_c, 2)

    var index_d = 0
    var expected_d = List[Int](2)
    for v in d.get_itter(StringKey("d")):
        print(v[])
        assert_equal(expected_d[index_d], v[])
        index_d += 1
    assert_equal(index_d, 0)

fn main()raises:
    test_add()
    test_s3_corpus()
    test_system_corpus()
    test_english_corpus()
    test_get_itter()


--- test_sparse_array.mojo ---

from generic_dict import SparseArray
from tensor import Tensor, TensorShape
from testing import assert_equal, assert_true


fn assert_equal_list[T: DType](lhs: List[Scalar[T]], rhs: List[Scalar[T]]) raises:
    assert_equal(len(lhs), len(rhs))
    for i in range(len(lhs)):
        assert_true(lhs[i] == rhs[i])


fn main() raises:
    var a = SparseArray[DType.int64](25)
    assert_equal(len(a.dense_values_list()), 0)
    a[23] = 15
    assert_equal(a.get(23).or_else(0), 15)
    assert_equal_list[DType.int64](a.dense_values_list(), List[Int64](15))
    a[1] = 45
    assert_equal(a.get(1).or_else(0), 45)
    assert_equal_list[DType.int64](a.dense_values_list(), List[Int64](45, 15))
    a[13] = 1
    assert_equal(a.get(13).or_else(0), 1)
    assert_equal_list[DType.int64](a.dense_values_list(), List[Int64](45, 1, 15))
    a[24] = 11
    assert_equal(a.get(24).or_else(0), 11)
    assert_equal_list[DType.int64](a.dense_values_list(), List[Int64](45, 1, 15, 11))
    a[2] = 0
    assert_equal(a.get(2).or_else(0), 0)
    assert_equal_list[DType.int64](a.dense_values_list(), List[Int64](45, 0, 1, 15, 11))
    a[53] = 5
    assert_equal(a.get(53).or_else(0), 5)
    assert_equal_list[DType.int64](a.dense_values_list(), List[Int64](45, 0, 1, 15, 11, 5))
    a[0] = 33
    assert_equal(a.get(0).or_else(0), 33)
    assert_equal_list[DType.int64](a.dense_values_list(), List[Int64](33, 45, 0, 1, 15, 11, 5))
    a[53] = 49
    assert_equal(a.get(53).or_else(0), 49)
    assert_equal_list[DType.int64](a.dense_values_list(), List[Int64](33, 45, 0, 1, 15, 11, 49))


--- test_string_dict.mojo ---

from string_dict import Dict
from testing import assert_equal

from corpora import *

fn test_simple_manipulations() raises:
    var d = Dict[Int, KeyCountType=DType.uint8, KeyOffsetType=DType.uint16]()
    var corpus = s3_action_names()
    for i in range(len(corpus)):
        d.put(corpus[i], i)
    
    assert_equal(len(d), 143)
    assert_equal(d.get("CopyObject", -1), 2)
    
    d.delete("CopyObject")
    assert_equal(d.get("CopyObject", -1), -1)
    assert_equal(len(d), 142)
    
    d.put("CopyObjects", 256)
    assert_equal(d.get("CopyObjects", -1), 256)
    assert_equal(d.get("CopyObject", -1), -1)
    assert_equal(len(d), 143)

    d.put("CopyObject", 257)
    assert_equal(d.get("CopyObject", -1), 257)
    assert_equal(len(d), 144)

    _ = d

fn test_simple_manipulations_on_non_destructive() raises:
    var d = Dict[Int, KeyCountType=DType.uint8, KeyOffsetType=DType.uint16, destructive=False]()
    var corpus = s3_action_names()
    for i in range(len(corpus)):
        d.put(corpus[i], i)
    
    assert_equal(len(d), 143)
    assert_equal(d.get("CopyObject", -1), 2)
    
    d.delete("CopyObject")
    assert_equal(d.get("CopyObject", -1), 2)
    assert_equal(len(d), 143)
    
    d.put("CopyObjects", 256)
    assert_equal(d.get("CopyObjects", -1), 256)
    assert_equal(d.get("CopyObject", -1), 2)
    assert_equal(len(d), 144)

    d.put("CopyObject", 257)
    assert_equal(d.get("CopyObject", -1), 257)
    assert_equal(len(d), 144)

fn test_simple_manipulations_non_caching() raises:
    var d = Dict[
        Int, 
        KeyCountType=DType.uint8, 
        KeyOffsetType=DType.uint16, 
        caching_hashes=False
    ]()
    var corpus = s3_action_names()
    for i in range(len(corpus)):
        d.put(corpus[i], i)
    assert_equal(len(d), 143)
    assert_equal(d.get("CopyObject", -1), 2)
    
    d.delete("CopyObject")
    assert_equal(d.get("CopyObject", -1), -1)
    assert_equal(len(d), 142)
    
    d.put("CopyObjects", 256)
    assert_equal(d.get("CopyObjects", -1), 256)
    assert_equal(d.get("CopyObject", -1), -1)
    assert_equal(len(d), 143)

    d.put("CopyObject", 257)
    assert_equal(d.get("CopyObject", -1), 257)
    assert_equal(len(d), 144)

    _ = d

@value
struct MyInt:
    var value: Int

fn test_upsert() raises:
    var d1 = Dict[MyInt, KeyCountType=DType.uint8, KeyOffsetType=DType.uint16]()
    var corpus = s3_action_names()
    
    fn inc(value: Optional[MyInt]) -> MyInt:
        return MyInt(value.or_else(MyInt(0)).value + 1)

    for i in range(len(corpus)):
        d1.upsert(corpus[i], inc)

    # Does not work probably because of Int is a register passable type
    # var d2 = Dict[Int, KeyCountType=DType.uint8, KeyOffsetType=DType.uint16]()

    # fn inc2(value: Optional[Int]) -> Int:
    #     return value.or_else(0) + 1

    # for i in range(len(corpus)):
    #     d2.upsert(corpus[i], inc2) 

fn test_clear() raises:
    var d = Dict[Int]()
    d.put("a", 1)
    d.put("b", 1)
    assert_equal(d.get("a", 0), 1)
    assert_equal(d.get("b", 0), 1)
    d.clear()
    d.put("a", 2)
    assert_equal(d.get("a", 0), 2)
    assert_equal(d.get("b", 0), 0)


fn main()raises:
    test_simple_manipulations()
    test_simple_manipulations_on_non_destructive()
    test_simple_manipulations_non_caching()
    test_upsert()
    test_clear()



--- .gitignore ---

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
#   For a library or package, you might want to ignore these files since the code is
#   intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# poetry
#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
#   This is especially recommended for binary packages to ensure reproducibility, and is more
#   commonly ignored for libraries.
#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock

# pdm
#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
#   in version control.
#   https://pdm.fming.dev/#use-with-ide
.pdm.toml
.pdm-python
.pdm-build/

# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

# PyCharm
#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
#  and can be added to the global gitignore or merged into this file.  For a more nuclear
#  option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/


--- LICENSE ---

MIT License

Copyright (c) 2023 Mojo Packaging Authority and Association

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


--- README.md ---

# menv
Mojo venv

### Installation and Usage

- `git clone git@github.com:mojopaa/menv.git`
- `cd menv`
- `pip install pdm`
- `pdm install -d`
- Open venv:
    - Windows: `.venv\Scripts\activate`
    - Linux: `. .venv/bin/activate`
- Run `menv -h`
- Run `menv .test`
- Run `source .test/bin/mactivate`
- Test with `which mojo`


### References

- [PEP 405](https://peps.python.org/pep-0405/)

--- notes/reproducer.py ---

import click
from click.testing import CliRunner


@click.command()
@click.option(
    "--without-scm-ignore-files",
    "scm_ignore_files",
    is_flag=True,
    type=frozenset,
    flag_value=frozenset(),
    default=frozenset(["git"]),
    help="Skips adding SCM ignore files to the environment "
    "directory (Git is supported by default).",
)
def rcli(scm_ignore_files):
    print(f"{scm_ignore_files = }")
    print(f"{type(scm_ignore_files) = }")


def test_rcli():
    runner = CliRunner()
    result = runner.invoke(rcli)
    assert result.exit_code == 0
    print(result.output)


if __name__ == "__main__":
    test_rcli()


--- pdm.lock ---

# This file is @generated by PDM.
# It is not intended for manual editing.

[metadata]
groups = ["default", "dev"]
cross_platform = true
static_urls = false
lock_version = "4.3"
content_hash = "sha256:8d9fa2594a770712bba532cfb57414a4325989d851109e2cffcee60892698bfe"

[[package]]
name = "appnope"
version = "0.1.3"
summary = "Disable App Nap on macOS >= 10.9"
files = [
    {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"},
    {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"},
]

[[package]]
name = "astroid"
version = "2.15.6"
requires_python = ">=3.7.2"
summary = "An abstract syntax tree for Python with inference support."
dependencies = [
    "lazy-object-proxy>=1.4.0",
    "typing-extensions>=4.0.0; python_version < \"3.11\"",
    "wrapt<2,>=1.11; python_version < \"3.11\"",
    "wrapt<2,>=1.14; python_version >= \"3.11\"",
]
files = [
    {file = "astroid-2.15.6-py3-none-any.whl", hash = "sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c"},
    {file = "astroid-2.15.6.tar.gz", hash = "sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd"},
]

[[package]]
name = "asttokens"
version = "2.4.0"
summary = "Annotate AST trees with source code positions"
dependencies = [
    "six>=1.12.0",
]
files = [
    {file = "asttokens-2.4.0-py2.py3-none-any.whl", hash = "sha256:cf8fc9e61a86461aa9fb161a14a0841a03c405fa829ac6b202670b3495d2ce69"},
    {file = "asttokens-2.4.0.tar.gz", hash = "sha256:2e0171b991b2c959acc6c49318049236844a5da1d65ba2672c4880c1c894834e"},
]

[[package]]
name = "backcall"
version = "0.2.0"
summary = "Specifications for callback functions passed in to an API"
files = [
    {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
    {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
]

[[package]]
name = "black"
version = "23.9.1"
requires_python = ">=3.8"
summary = "The uncompromising code formatter."
dependencies = [
    "click>=8.0.0",
    "mypy-extensions>=0.4.3",
    "packaging>=22.0",
    "pathspec>=0.9.0",
    "platformdirs>=2",
    "tomli>=1.1.0; python_version < \"3.11\"",
    "typing-extensions>=4.0.1; python_version < \"3.11\"",
]
files = [
    {file = "black-23.9.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f"},
    {file = "black-23.9.1-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe"},
    {file = "black-23.9.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186"},
    {file = "black-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f"},
    {file = "black-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300"},
    {file = "black-23.9.1-py3-none-any.whl", hash = "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9"},
    {file = "black-23.9.1.tar.gz", hash = "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d"},
]

[[package]]
name = "click"
version = "8.1.7"
requires_python = ">=3.7"
summary = "Composable command line interface toolkit"
dependencies = [
    "colorama; platform_system == \"Windows\"",
]
files = [
    {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
    {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
]

[[package]]
name = "colorama"
version = "0.4.6"
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
summary = "Cross-platform colored terminal text."
files = [
    {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
    {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]

[[package]]
name = "decorator"
version = "5.1.1"
requires_python = ">=3.5"
summary = "Decorators for Humans"
files = [
    {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
    {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
]

[[package]]
name = "dill"
version = "0.3.7"
requires_python = ">=3.7"
summary = "serialize all of Python"
files = [
    {file = "dill-0.3.7-py3-none-any.whl", hash = "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e"},
    {file = "dill-0.3.7.tar.gz", hash = "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03"},
]

[[package]]
name = "exceptiongroup"
version = "1.1.3"
requires_python = ">=3.7"
summary = "Backport of PEP 654 (exception groups)"
files = [
    {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"},
    {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"},
]

[[package]]
name = "executing"
version = "1.2.0"
summary = "Get the currently executing AST node of a frame, and other information"
files = [
    {file = "executing-1.2.0-py2.py3-none-any.whl", hash = "sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc"},
    {file = "executing-1.2.0.tar.gz", hash = "sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107"},
]

[[package]]
name = "ghp-import"
version = "2.1.0"
summary = "Copy your docs directly to the gh-pages branch."
dependencies = [
    "python-dateutil>=2.8.1",
]
files = [
    {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"},
    {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"},
]

[[package]]
name = "iniconfig"
version = "2.0.0"
requires_python = ">=3.7"
summary = "brain-dead simple config-ini parsing"
files = [
    {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
    {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
]

[[package]]
name = "ipython"
version = "8.15.0"
requires_python = ">=3.9"
summary = "IPython: Productive Interactive Computing"
dependencies = [
    "appnope; sys_platform == \"darwin\"",
    "backcall",
    "colorama; sys_platform == \"win32\"",
    "decorator",
    "exceptiongroup; python_version < \"3.11\"",
    "jedi>=0.16",
    "matplotlib-inline",
    "pexpect>4.3; sys_platform != \"win32\"",
    "pickleshare",
    "prompt-toolkit!=3.0.37,<3.1.0,>=3.0.30",
    "pygments>=2.4.0",
    "stack-data",
    "traitlets>=5",
]
files = [
    {file = "ipython-8.15.0-py3-none-any.whl", hash = "sha256:45a2c3a529296870a97b7de34eda4a31bee16bc7bf954e07d39abe49caf8f887"},
    {file = "ipython-8.15.0.tar.gz", hash = "sha256:2baeb5be6949eeebf532150f81746f8333e2ccce02de1c7eedde3f23ed5e9f1e"},
]

[[package]]
name = "isort"
version = "5.12.0"
requires_python = ">=3.8.0"
summary = "A Python utility / library to sort Python imports."
files = [
    {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"},
    {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"},
]

[[package]]
name = "jedi"
version = "0.19.0"
requires_python = ">=3.6"
summary = "An autocompletion tool for Python that can be used for text editors."
dependencies = [
    "parso<0.9.0,>=0.8.3",
]
files = [
    {file = "jedi-0.19.0-py2.py3-none-any.whl", hash = "sha256:cb8ce23fbccff0025e9386b5cf85e892f94c9b822378f8da49970471335ac64e"},
    {file = "jedi-0.19.0.tar.gz", hash = "sha256:bcf9894f1753969cbac8022a8c2eaee06bfa3724e4192470aaffe7eb6272b0c4"},
]

[[package]]
name = "jinja2"
version = "3.1.2"
requires_python = ">=3.7"
summary = "A very fast and expressive template engine."
dependencies = [
    "MarkupSafe>=2.0",
]
files = [
    {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
    {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
]

[[package]]
name = "lazy-object-proxy"
version = "1.9.0"
requires_python = ">=3.7"
summary = "A fast and thorough lazy object proxy."
files = [
    {file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"},
    {file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"},
    {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"},
    {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"},
    {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"},
    {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"},
    {file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"},
    {file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"},
]

[[package]]
name = "markdown"
version = "3.4.4"
requires_python = ">=3.7"
summary = "Python implementation of John Gruber's Markdown."
files = [
    {file = "Markdown-3.4.4-py3-none-any.whl", hash = "sha256:a4c1b65c0957b4bd9e7d86ddc7b3c9868fb9670660f6f99f6d1bca8954d5a941"},
    {file = "Markdown-3.4.4.tar.gz", hash = "sha256:225c6123522495d4119a90b3a3ba31a1e87a70369e03f14799ea9c0d7183a3d6"},
]

[[package]]
name = "markupsafe"
version = "2.1.3"
requires_python = ">=3.7"
summary = "Safely add untrusted strings to HTML/XML markup."
files = [
    {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"},
    {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"},
    {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"},
    {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"},
    {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"},
    {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"},
    {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"},
    {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"},
    {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"},
    {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"},
    {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"},
    {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"},
    {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"},
    {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"},
    {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"},
    {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"},
    {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"},
    {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"},
    {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"},
    {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"},
    {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"},
]

[[package]]
name = "matplotlib-inline"
version = "0.1.6"
requires_python = ">=3.5"
summary = "Inline Matplotlib backend for Jupyter"
dependencies = [
    "traitlets",
]
files = [
    {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"},
    {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"},
]

[[package]]
name = "mccabe"
version = "0.7.0"
requires_python = ">=3.6"
summary = "McCabe checker, plugin for flake8"
files = [
    {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
    {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
]

[[package]]
name = "mergedeep"
version = "1.3.4"
requires_python = ">=3.6"
summary = "A deep merge function for 🐍."
files = [
    {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"},
    {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"},
]

[[package]]
name = "mkdocs"
version = "1.5.2"
requires_python = ">=3.7"
summary = "Project documentation with Markdown."
dependencies = [
    "click>=7.0",
    "colorama>=0.4; platform_system == \"Windows\"",
    "ghp-import>=1.0",
    "jinja2>=2.11.1",
    "markdown>=3.2.1",
    "markupsafe>=2.0.1",
    "mergedeep>=1.3.4",
    "packaging>=20.5",
    "pathspec>=0.11.1",
    "platformdirs>=2.2.0",
    "pyyaml-env-tag>=0.1",
    "pyyaml>=5.1",
    "watchdog>=2.0",
]
files = [
    {file = "mkdocs-1.5.2-py3-none-any.whl", hash = "sha256:60a62538519c2e96fe8426654a67ee177350451616118a41596ae7c876bb7eac"},
    {file = "mkdocs-1.5.2.tar.gz", hash = "sha256:70d0da09c26cff288852471be03c23f0f521fc15cf16ac89c7a3bfb9ae8d24f9"},
]

[[package]]
name = "mypy-extensions"
version = "1.0.0"
requires_python = ">=3.5"
summary = "Type system extensions for programs checked with the mypy type checker."
files = [
    {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
    {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
]

[[package]]
name = "packaging"
version = "23.1"
requires_python = ">=3.7"
summary = "Core utilities for Python packages"
files = [
    {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"},
    {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"},
]

[[package]]
name = "parso"
version = "0.8.3"
requires_python = ">=3.6"
summary = "A Python Parser"
files = [
    {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"},
    {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"},
]

[[package]]
name = "pathspec"
version = "0.11.2"
requires_python = ">=3.7"
summary = "Utility library for gitignore style pattern matching of file paths."
files = [
    {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"},
    {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"},
]

[[package]]
name = "pexpect"
version = "4.8.0"
summary = "Pexpect allows easy control of interactive console applications."
dependencies = [
    "ptyprocess>=0.5",
]
files = [
    {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"},
    {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"},
]

[[package]]
name = "pickleshare"
version = "0.7.5"
summary = "Tiny 'shelve'-like database with concurrency support"
files = [
    {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"},
    {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
]

[[package]]
name = "platformdirs"
version = "3.10.0"
requires_python = ">=3.7"
summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
files = [
    {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"},
    {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"},
]

[[package]]
name = "pluggy"
version = "1.3.0"
requires_python = ">=3.8"
summary = "plugin and hook calling mechanisms for python"
files = [
    {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"},
    {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"},
]

[[package]]
name = "prompt-toolkit"
version = "3.0.39"
requires_python = ">=3.7.0"
summary = "Library for building powerful interactive command lines in Python"
dependencies = [
    "wcwidth",
]
files = [
    {file = "prompt_toolkit-3.0.39-py3-none-any.whl", hash = "sha256:9dffbe1d8acf91e3de75f3b544e4842382fc06c6babe903ac9acb74dc6e08d88"},
    {file = "prompt_toolkit-3.0.39.tar.gz", hash = "sha256:04505ade687dc26dc4284b1ad19a83be2f2afe83e7a828ace0c72f3a1df72aac"},
]

[[package]]
name = "ptyprocess"
version = "0.7.0"
summary = "Run a subprocess in a pseudo terminal"
files = [
    {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
    {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
]

[[package]]
name = "pure-eval"
version = "0.2.2"
summary = "Safely evaluate AST nodes without side effects"
files = [
    {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"},
    {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"},
]

[[package]]
name = "pygments"
version = "2.16.1"
requires_python = ">=3.7"
summary = "Pygments is a syntax highlighting package written in Python."
files = [
    {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"},
    {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"},
]

[[package]]
name = "pylint"
version = "2.17.5"
requires_python = ">=3.7.2"
summary = "python code static checker"
dependencies = [
    "astroid<=2.17.0-dev0,>=2.15.6",
    "colorama>=0.4.5; sys_platform == \"win32\"",
    "dill>=0.2; python_version < \"3.11\"",
    "dill>=0.3.6; python_version >= \"3.11\"",
    "isort<6,>=4.2.5",
    "mccabe<0.8,>=0.6",
    "platformdirs>=2.2.0",
    "tomli>=1.1.0; python_version < \"3.11\"",
    "tomlkit>=0.10.1",
]
files = [
    {file = "pylint-2.17.5-py3-none-any.whl", hash = "sha256:73995fb8216d3bed149c8d51bba25b2c52a8251a2c8ac846ec668ce38fab5413"},
    {file = "pylint-2.17.5.tar.gz", hash = "sha256:f7b601cbc06fef7e62a754e2b41294c2aa31f1cb659624b9a85bcba29eaf8252"},
]

[[package]]
name = "pytest"
version = "7.4.2"
requires_python = ">=3.7"
summary = "pytest: simple powerful testing with Python"
dependencies = [
    "colorama; sys_platform == \"win32\"",
    "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"",
    "iniconfig",
    "packaging",
    "pluggy<2.0,>=0.12",
    "tomli>=1.0.0; python_version < \"3.11\"",
]
files = [
    {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"},
    {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"},
]

[[package]]
name = "python-dateutil"
version = "2.8.2"
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
summary = "Extensions to the standard Python datetime module"
dependencies = [
    "six>=1.5",
]
files = [
    {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
    {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
]

[[package]]
name = "pyyaml"
version = "6.0.1"
requires_python = ">=3.6"
summary = "YAML parser and emitter for Python"
files = [
    {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
    {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
    {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
    {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
    {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
    {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
    {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
    {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
    {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
    {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
    {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
    {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
    {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
    {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
    {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
]

[[package]]
name = "pyyaml-env-tag"
version = "0.1"
requires_python = ">=3.6"
summary = "A custom YAML tag for referencing environment variables in YAML files. "
dependencies = [
    "pyyaml",
]
files = [
    {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"},
    {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"},
]

[[package]]
name = "ruff"
version = "0.0.289"
requires_python = ">=3.7"
summary = "An extremely fast Python linter, written in Rust."
files = [
    {file = "ruff-0.0.289-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:c9a89d748e90c840bac9c37afe90cf13a5bfd460ca02ea93dad9d7bee3af03b4"},
    {file = "ruff-0.0.289-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:7f7396c6ea01ba332a6ad9d47642bac25d16bd2076aaa595b001f58b2f32ff05"},
    {file = "ruff-0.0.289-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7180de86c8ecd39624dec1699136f941c07e723201b4ce979bec9e7c67b40ad2"},
    {file = "ruff-0.0.289-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:73f37c65508203dd01a539926375a10243769c20d4fcab3fa6359cd3fbfc54b7"},
    {file = "ruff-0.0.289-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c14abcd7563b5c80be2dd809eeab20e4aa716bf849860b60a22d87ddf19eb88"},
    {file = "ruff-0.0.289-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:91b6d63b6b46d4707916472c91baa87aa0592e73f62a80ff55efdf6c0668cfd6"},
    {file = "ruff-0.0.289-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6479b8c4be3c36046c6c92054762b276fa0fddb03f6b9a310fbbf4c4951267fd"},
    {file = "ruff-0.0.289-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c5424318c254bcb091cb67e140ec9b9f7122074e100b06236f252923fb41e767"},
    {file = "ruff-0.0.289-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4daa90865796aedcedf0d8897fdd4cd09bf0ddd3504529a4ccf211edcaff3c7d"},
    {file = "ruff-0.0.289-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:8057e8ab0016c13b9419bad119e854f881e687bd96bc5e2d52c8baac0f278a44"},
    {file = "ruff-0.0.289-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7eebfab2e6a6991908ff1bf82f2dc1e5095fc7e316848e62124526837b445f4d"},
    {file = "ruff-0.0.289-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ebc7af550018001a7fb39ca22cdce20e1a0de4388ea4a007eb5c822f6188c297"},
    {file = "ruff-0.0.289-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6e4e6eccb753efe760ba354fc8e9f783f6bba71aa9f592756f5bd0d78db898ed"},
    {file = "ruff-0.0.289-py3-none-win32.whl", hash = "sha256:bbb3044f931c09cf17dbe5b339896eece0d6ac10c9a86e172540fcdb1974f2b7"},
    {file = "ruff-0.0.289-py3-none-win_amd64.whl", hash = "sha256:6d043c5456b792be2615a52f16056c3cf6c40506ce1f2d6f9d3083cfcb9eeab6"},
    {file = "ruff-0.0.289-py3-none-win_arm64.whl", hash = "sha256:04a720bcca5e987426bb14ad8b9c6f55e259ea774da1cbeafe71569744cfd20a"},
    {file = "ruff-0.0.289.tar.gz", hash = "sha256:2513f853b0fc42f0339b7ab0d2751b63ce7a50a0032d2689b54b2931b3b866d7"},
]

[[package]]
name = "six"
version = "1.16.0"
requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
summary = "Python 2 and 3 compatibility utilities"
files = [
    {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
    {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]

[[package]]
name = "stack-data"
version = "0.6.2"
summary = "Extract data from python stack frames and tracebacks for informative displays"
dependencies = [
    "asttokens>=2.1.0",
    "executing>=1.2.0",
    "pure-eval",
]
files = [
    {file = "stack_data-0.6.2-py3-none-any.whl", hash = "sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8"},
    {file = "stack_data-0.6.2.tar.gz", hash = "sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815"},
]

[[package]]
name = "tomli"
version = "2.0.1"
requires_python = ">=3.7"
summary = "A lil' TOML parser"
files = [
    {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
    {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]

[[package]]
name = "tomlkit"
version = "0.12.1"
requires_python = ">=3.7"
summary = "Style preserving TOML library"
files = [
    {file = "tomlkit-0.12.1-py3-none-any.whl", hash = "sha256:712cbd236609acc6a3e2e97253dfc52d4c2082982a88f61b640ecf0817eab899"},
    {file = "tomlkit-0.12.1.tar.gz", hash = "sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86"},
]

[[package]]
name = "traitlets"
version = "5.10.0"
requires_python = ">=3.8"
summary = "Traitlets Python configuration system"
files = [
    {file = "traitlets-5.10.0-py3-none-any.whl", hash = "sha256:417745a96681fbb358e723d5346a547521f36e9bd0d50ba7ab368fff5d67aa54"},
    {file = "traitlets-5.10.0.tar.gz", hash = "sha256:f584ea209240466e66e91f3c81aa7d004ba4cf794990b0c775938a1544217cd1"},
]

[[package]]
name = "typing-extensions"
version = "4.7.1"
requires_python = ">=3.7"
summary = "Backported and Experimental Type Hints for Python 3.7+"
files = [
    {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"},
    {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"},
]

[[package]]
name = "watchdog"
version = "3.0.0"
requires_python = ">=3.7"
summary = "Filesystem events monitoring"
files = [
    {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae"},
    {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9"},
    {file = "watchdog-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7"},
    {file = "watchdog-3.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100"},
    {file = "watchdog-3.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346"},
    {file = "watchdog-3.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64"},
    {file = "watchdog-3.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a"},
    {file = "watchdog-3.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44"},
    {file = "watchdog-3.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a"},
    {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709"},
    {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83"},
    {file = "watchdog-3.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d"},
    {file = "watchdog-3.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33"},
    {file = "watchdog-3.0.0-py3-none-win32.whl", hash = "sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f"},
    {file = "watchdog-3.0.0-py3-none-win_amd64.whl", hash = "sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c"},
    {file = "watchdog-3.0.0-py3-none-win_ia64.whl", hash = "sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759"},
    {file = "watchdog-3.0.0.tar.gz", hash = "sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9"},
]

[[package]]
name = "wcwidth"
version = "0.2.6"
summary = "Measures the displayed width of unicode strings in a terminal"
files = [
    {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"},
    {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"},
]

[[package]]
name = "wrapt"
version = "1.15.0"
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
summary = "Module for decorators, wrappers and monkey patching."
files = [
    {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"},
    {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"},
    {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"},
    {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"},
    {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"},
    {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"},
    {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"},
    {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"},
    {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"},
    {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"},
    {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"},
    {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"},
]


--- pyproject.toml ---

[project]
name = "menv"
version = "0.1.0"
description = "Mojo venv"
authors = [
    {name = "Drunkwcodes", email = "drunkwcodes@gmail.com"},
]
dependencies = [
    "click>=8.1.7",
    "tomlkit>=0.12.1",
]
requires-python = ">=3.10"
readme = "README.md"
license = {text = "MIT"}

[build-system]
requires = ["pdm-backend"]
build-backend = "pdm.backend"

[tool.pdm.dev-dependencies]
dev = [
    "ipython>=8.15.0",
    "pytest>=7.4.2",
    "mkdocs>=1.5.2",
    "ruff>=0.0.289",
    "black>=23.9.1",
    "pylint>=2.17.5",
]

[project.scripts]
menv = "menv.cli:cli"


--- src/menv/__init__.py ---

__version__ = "0.1.0"

--- src/menv/builder.py ---

import configparser
import logging
import os
import shutil
import subprocess
import sys
import types
from pathlib import Path

import tomlkit
from tomlkit.toml_file import TOMLFile

MODULAR_NAME = ".modular"
MODULAR_PKG_FOLDER = "pkg"
MODULAR_PKG_NAME = "packages.modular.com_mojo"
MODULAR_CONFIG_NAME = "modular.cfg"


MODULAR_DIR = Path.home() / MODULAR_NAME
MODULAR_CONFIG = MODULAR_DIR / MODULAR_CONFIG_NAME
MOJO_PKG_DIR = MODULAR_DIR / MODULAR_PKG_FOLDER / MODULAR_PKG_NAME
MOJO_BIN_DIR = MOJO_PKG_DIR / "bin"
MOJO_LIB_DIR = MOJO_PKG_DIR / "lib"
MOJO_IMPORT_DIR = MOJO_LIB_DIR / "mojo"
MOJO_EXECUTABLE = MOJO_BIN_DIR / "mojo"

logger = logging.getLogger(__name__)


def create_if_needed(d):
    if not os.path.exists(d):
        os.makedirs(d)
    elif os.path.islink(d) or os.path.isfile(d):
        raise ValueError("Unable to create directory %r" % d)


def clear_directory(path):
    for fn in os.listdir(path):
        fn = os.path.join(path, fn)
        if os.path.islink(fn) or os.path.isfile(fn):
            os.remove(fn)
        elif os.path.isdir(fn):
            shutil.rmtree(fn)


def change_config(path: str, section: str, key: str, value: str):
    config = configparser.ConfigParser()
    config.read(path)

    config[section][key] = value

    with open(path, "w", encoding="utf-8") as f:
        config.write(f)


class MojoEnvBuilder:
    def __init__(
        self,
        system_site_packages=False,
        clear=False,
        symlinks=False,
        upgrade=False,
        prompt=None,
        upgrade_deps=False,
        scm_ignore_files=True,
    ):
        self.system_site_packages = system_site_packages
        self.clear = clear
        self.symlinks = symlinks
        self.upgrade = upgrade
        self.orig_prompt = prompt
        if prompt == ".":  # see bpo-38901
            prompt = os.path.basename(os.getcwd())
        self.prompt = prompt
        self.upgrade_deps = upgrade_deps
        self.scm_ignore_files = scm_ignore_files

    def create(self, env_dir):
        """
        Create a virtual environment in a directory.

        :param env_dir: The target directory to create an environment in.

        """
        env_dir = os.path.abspath(env_dir)
        context = self.ensure_directories(env_dir)
        # for scm in self.scm_ignore_files:
        #     getattr(self, f"create_{scm}_ignore_file")(context)
        # See issue 24875. We need system_site_packages to be False
        # until after pip is installed.
        true_system_site_packages = self.system_site_packages
        self.system_site_packages = False
        self.create_configuration(context)
        self.setup_mojo(context)
        # if self.with_pip:
        #     self._setup_pip(context)
        if not self.upgrade:
            self.setup_scripts(context)
            self.post_setup(context)
        if true_system_site_packages:
            # We had set it to False before, now
            # restore it and rewrite the configuration
            self.system_site_packages = True
            self.create_configuration(context)
        if self.upgrade_deps:
            self.upgrade_dependencies(context)
        if self.scm_ignore_files:
            self.create_git_ignore_file(context)

    def ensure_directories(self, env_dir: str | Path):
        """
        Create the directories for the environment.

        Returns a context object which holds paths in the environment,
        for use by subsequent logic.
        """
        if os.pathsep in os.fspath(env_dir):
            raise ValueError(
                f"Refusing to create a venv in {env_dir} because "
                f"it contains the PATH separator {os.pathsep}."
            )

        if os.path.exists(env_dir) and self.clear:
            clear_directory(env_dir)

        context = types.SimpleNamespace()
        context.env_dir = str(env_dir)
        context.env_name = os.path.split(env_dir)[1]
        prompt = self.prompt if self.prompt is not None else context.env_name
        context.prompt = "(%s) " % prompt
        create_if_needed(env_dir)

        context.mojo_dir = str(MOJO_BIN_DIR)  # TODO: use findmojo
        context.mojo_exe = "mojo"

        # venv paths
        venv_modular_dir = Path(env_dir) / MODULAR_NAME
        venv_modular_cfg = venv_modular_dir / MODULAR_CONFIG_NAME
        venv_pkg_dir = venv_modular_dir / MODULAR_PKG_FOLDER / MODULAR_PKG_NAME

        bin_name = "bin"  # Mojo bin name
        venv_bin_dir = venv_pkg_dir / bin_name
        venv_lib_dir = venv_pkg_dir / "lib"
        venv_mojo_excutable = venv_bin_dir / "mojo"

        context.executable = MOJO_EXECUTABLE
        context.bin_name = bin_name
        context.pkg_dir = str(venv_pkg_dir)
        context.bin_path = str(venv_bin_dir)
        context.lib_path = str(venv_lib_dir)
        context.env_exe = str(venv_mojo_excutable)
        context.env_cfg = str(venv_modular_cfg)

        # venv bin path, reference: /usr/lib/python3.10/venv/__init__.py
        if sys.platform == "win32":
            py_venv_biname = "Scripts"
        else:
            py_venv_biname = "bin"

        context.py_venv_binpath = str(Path(env_dir) / py_venv_biname)

        create_if_needed(venv_bin_dir)
        create_if_needed(venv_lib_dir)

        return context

    def create_configuration(self, context):
        """
        Create a configuration file indicating where the environment's Python
        was copied from, and whether the system site-packages should be made
        available in the environment.

        Args:
            context: The context object containing information about the environment.

        Returns:
            None
        """
        context.cfg_path = path = os.path.join(context.env_dir, "mojovenv.toml")

        cfg = tomlkit.document()
        mojo_tab = tomlkit.table()
        mojo_tab.add("home", context.mojo_dir)

        if self.system_site_packages:
            incl = True
        else:
            incl = False
        mojo_tab.add("include-system-site-packages", incl)

        # Read VERSION
        with open(MOJO_PKG_DIR / "VERSION") as f:
            version = f.read().strip()

        mojo_tab.add("version", version)

        if self.prompt is not None:
            mojo_tab.add("prompt", f"{self.prompt!r}")

        mojo_tab.add("mojo-executable", str(MOJO_EXECUTABLE))

        # build command
        args = []
        nt = os.name == "nt"
        if nt and self.symlinks:
            args.append("--symlinks")
        if not nt and not self.symlinks:
            args.append("--copies")
        if self.system_site_packages:
            args.append("--system-site-packages")
        if self.clear:
            args.append("--clear")
        if self.upgrade:
            args.append("--upgrade")
        if self.upgrade_deps:
            args.append("--upgrade-deps")
        if self.orig_prompt is not None:
            args.append(f'--prompt="{self.orig_prompt}"')
        if not self.scm_ignore_files:
            args.append("--without-scm-ignore-files")

        args.append(context.env_dir)
        args = " ".join(args)

        mojo_tab.add("command", f"menv {args}")
        cfg["mojo"] = mojo_tab
        TOMLFile(path).write(cfg)

    if os.name != "nt":

        def symlink_or_copy(self, src, dst, relative_symlinks_ok=False):
            """
            Try symlinking a file, and if that fails, fall back to copying.
            """
            force_copy = not self.symlinks
            if not force_copy:
                try:
                    if not os.path.islink(dst):  # can't link to itself!
                        if relative_symlinks_ok:
                            assert os.path.dirname(src) == os.path.dirname(dst)
                            os.symlink(os.path.basename(src), dst)
                        else:
                            os.symlink(src, dst)
                except Exception:  # may need to use a more specific exception
                    logger.warning("Unable to symlink %r to %r", src, dst)
                    force_copy = True
            if force_copy:
                shutil.copyfile(src, dst)

    else:

        def symlink_or_copy(self, src, dst, relative_symlinks_ok=False):
            """
            Try symlinking a file, and if that fails, fall back to copying.
            """
            bad_src = os.path.lexists(src) and not os.path.exists(src)
            if self.symlinks and not bad_src and not os.path.islink(dst):
                try:
                    if relative_symlinks_ok:
                        assert os.path.dirname(src) == os.path.dirname(dst)
                        os.symlink(os.path.basename(src), dst)
                    else:
                        os.symlink(src, dst)
                    return
                except Exception:  # may need to use a more specific exception
                    logger.warning("Unable to symlink %r to %r", src, dst)

            if not os.path.exists(src):
                if not bad_src:
                    logger.warning("Unable to copy %r", src)
                return

            shutil.copyfile(src, dst)

    def recursive_symlink_or_copy(self, src, dst, relative_symlinks_ok=False):
        for item in os.listdir(src):
            src_item = os.path.join(src, item)
            dst_item = os.path.join(dst, item)
            if os.path.isfile(src_item):
                self.symlink_or_copy(src_item, dst_item, relative_symlinks_ok)
                shutil.copymode(src_item, dst_item)
            elif os.path.isdir(src_item):
                os.makedirs(dst_item, exist_ok=True)
                self.recursive_symlink_or_copy(src_item, dst_item, relative_symlinks_ok)
            else:
                logger.warning(f"Skipping {src_item}")

    def create_git_ignore_file(self, context):
        """
        Create a .gitignore file in the environment directory.

        The contents of the file cause the entire environment directory to be
        ignored by git.
        """
        gitignore_path = os.path.join(context.env_dir, ".gitignore")
        with open(gitignore_path, "w", encoding="utf-8") as file:
            file.write(
                "# Created by venv; "
                "see https://docs.python.org/3/library/venv.html\n"
            )  # TODO: change this?
            file.write("*\n")

    def setup_mojo(self, context):
        """
        Set up a Mojo executable in the environment.

        Args:
            context (obj): The information for the environment creation request being processed.
        """
        binpath = context.bin_path
        libpath = context.lib_path
        path = context.env_exe
        copier = self.symlink_or_copy
        dirname = context.mojo_dir  # context.mojo_dir = str(MOJO_BIN_DIR)

        if os.name != "nt":
            # copy lib and bin to venv
            self.recursive_symlink_or_copy(MOJO_LIB_DIR, libpath)
            self.recursive_symlink_or_copy(MOJO_BIN_DIR, binpath)

            for bin_item in os.listdir(binpath):
                if not os.path.islink(os.path.join(binpath, bin_item)):
                    # Set the executable's permissions
                    os.chmod(os.path.join(binpath, bin_item), 0o755)

            # Create symbolic links for mojo executables
            for suffix in "mojo":
                path = os.path.join(binpath, suffix)
                if not os.path.exists(path):
                    # Make copies if symlinks are not wanted
                    copier(context.env_exe, path, relative_symlinks_ok=True)
                    if not os.path.islink(path):
                        os.chmod(path, 0o755)

            # Copy config and change import_path settings etc.
            copier(MODULAR_CONFIG, context.env_cfg)
            self.write_modular_cfg(context)

        else:
            pass  # TODO

    def write_modular_cfg(self, context):
        cfg_path = context.env_cfg  # context.env_cfg = str(venv_modular_cfg)
        libpath = context.lib_path  # str(venv_pkg_dir / "lib")

        change_config(
            cfg_path,
            section="mojo",
            key="import_path",
            value=os.path.join(libpath, "mojo"),
        )  # not working
        change_config(
            cfg_path,
            section="installed",
            key="packages_modular_com_mojo",
            value=context.pkg_dir,
        )
        # venv_pkg_config = os.path.join(context.pkg_dir, MODULAR_CONFIG_NAME)
        # print(venv_pkg_config)
        # change_config(venv_pkg_config, section="mojo", key="import_path", value=os.path.join(libpath, "mojo"))

    def replace_variables(self, text, context):
        """
        Replace variable placeholders in the script text with context-specific
        variables.

        Args:
            text (str): The text in which to replace placeholder variables.
            context (Context): The information for the environment creation request
                being processed.

        Returns:
            str: The text passed in, but with variables replaced.
        """
        # Replace '__VENV_DIR__' placeholder with context.env_dir
        text = text.replace("__VENV_DIR__", context.env_dir)  # abspath of venv

        # Replace '__VENV_NAME__' placeholder with context.env_name
        text = text.replace("__VENV_NAME__", context.env_name)  # stem of venv

        # Replace '__VENV_PROMPT__' placeholder with context.prompt
        text = text.replace("__VENV_PROMPT__", context.prompt)

        # Replace '__VENV_BIN_NAME__' placeholder with context.bin_name
        text = text.replace("__VENV_BIN_NAME__", context.bin_name)  # bin

        # Replace '__VENV_BIN_PATH__' placeholder with context.bin_path
        text = text.replace("__VENV_BIN_PATH__", context.bin_path)

        # Replace '__VENV_MOJO__' placeholder with context.env_exe
        text = text.replace("__VENV_MOJO__", context.env_exe)

        return text

    def setup_scripts(self, context):
        """
        Set up scripts into the created environment from a directory.

        This method installs the default scripts into the environment
        being created. You can prevent the default installation by overriding
        this method if you really need to, or if you need to specify
        a different location for the scripts to install. By default, the
        'scripts' directory in the venv package is used as the source of
        scripts to install.
        """
        path = str(Path(__file__).with_name("scripts").absolute())
        self.install_scripts(context, path)

    def install_scripts(self, context, path):
        """
        Install scripts into the created environment from a directory.

        Args:
            context: The information for the environment creation request
                being processed.
            path: Absolute pathname of a directory containing script.
                Scripts in the 'common' subdirectory of this directory,
                and those in the directory named for the platform
                being run on, are installed in the created environment.
                Placeholder variables are replaced with environment-
                specific values.
        """
        # binpath = context.bin_path  # Get the bin path from the context
        binpath = context.py_venv_binpath  # Get the bin path from the context
        plen = len(path)  # Get the length of the path
        for root, dirs, files in os.walk(path):
            if root == path:  # At top-level, remove irrelevant dirs
                for d in dirs[:]:
                    if d not in ("common", os.name):
                        dirs.remove(d)
                continue  # Ignore files in top level
            for f in files:
                if (
                    os.name == "nt"
                    and f.startswith("python")
                    and f.endswith((".exe", ".pdb"))
                ):
                    continue  # Skip files that start with 'python' and end with '.exe' or '.pdb' on Windows
                srcfile = os.path.join(root, f)  # Get the source file path
                suffix = root[plen:].split(os.sep)[
                    2:
                ]  # Get the relative path of the file
                if not suffix:
                    dstdir = binpath  # If there is no suffix, set the destination directory as binpath
                else:
                    dstdir = os.path.join(
                        binpath, *suffix
                    )  # Set the destination directory as binpath + suffix
                if not os.path.exists(dstdir):
                    os.makedirs(
                        dstdir
                    )  # Create the destination directory if it does not exist
                dstfile = os.path.join(dstdir, f)  # Get the destination file path
                with open(srcfile, "rb") as f:
                    data = f.read()  # Read the source file data
                if not srcfile.endswith((".exe", ".pdb")):
                    try:
                        data = data.decode("utf-8")  # Decode the data as utf-8
                        data = self.replace_variables(
                            data, context
                        )  # Replace the placeholder variables with environment-specific values
                        data = data.encode("utf-8")  # Encode the data as utf-8
                    except UnicodeError as e:
                        data = None
                        logger.error(f"UnicodeError: {e}")
                        logger.warning(
                            "unable to copy script %r, " "may be binary: %s", srcfile, e
                        )  # Log a warning if unable to copy script due to UnicodeError
                if data is not None:
                    with open(dstfile, "wb") as f:
                        f.write(data)  # Write the data to the destination file
                    shutil.copymode(
                        srcfile, dstfile
                    )  # Copy the permissions from the source file to the destination file

    def upgrade_dependencies(self, context):
        logger.warning("TODO: upgrade CORE_VENV_DEPS")
        logger.warning("NOP right now.")

    def post_setup(self, context):
        """
        Hook for post-setup modification of the venv.
        Subclasses may install additional packages or scripts here,
        add activation shell scripts, etc.

        Parameters:
          - context: The information for the environment creation request
                     being processed.

        No changes are made in the function body.
        """

        pass


def create(
    env_dir,
    system_site_packages=False,
    clear=False,
    symlinks=False,
    prompt=None,
    upgrade_deps=False,
    *,
    scm_ignore_files=True,
):
    """Create a virtual environment in a directory."""
    builder = MojoEnvBuilder(
        system_site_packages=system_site_packages,
        clear=clear,
        symlinks=symlinks,
        prompt=prompt,
        upgrade_deps=upgrade_deps,
        scm_ignore_files=scm_ignore_files,
    )
    builder.create(env_dir)


if __name__ == "__main__":
    # m = MojoEnvBuilder()
    # c = m.ensure_directories(".asdf")
    # m.create_configuration(c)
    # m.setup_mojo(c)
    # m.create_git_ignore_file(c)
    create(".asdf")


--- src/menv/cli.py ---

import os
from venv import EnvBuilder

import click

from .builder import MojoEnvBuilder
from .utils import CORE_VENV_DEPS

if os.name == "nt":
    use_symlinks = False
else:
    use_symlinks = True


@click.command(context_settings=dict(help_option_names=["-h", "--help"]))
@click.argument("dirs", nargs=-1)
@click.option(
    "--system-site-packages",
    "system_site",
    is_flag=True,
    help="Give the virtual environment access to the " "system site-packages dir.",
)
@click.option(
    "--symlinks",
    is_flag=True,
    default=use_symlinks,
    help="Try to use symlinks rather than copies, "
    "when symlinks are not the default for "
    "the platform.",
)
@click.option(
    "--copies",
    is_flag=True,
    help="Try to use copies rather than symlinks, "
    "even when symlinks are the default for "
    "the platform.",
)
@click.option(
    "--clear",
    is_flag=True,
    help="Delete the contents of the "
    "environment directory if it "
    "already exists, before "
    "environment creation.",
)
@click.option(
    "--upgrade",
    is_flag=True,
    help="Upgrade the environment "
    "directory to use this version "
    "of Python, assuming Python "
    "has been upgraded in-place.",
)
@click.option(
    "--without-pip",
    "with_pip",
    is_flag=True,
    default=True,
    flag_value=False,
    help="Skips installing or upgrading pip in the "
    "virtual environment (pip is bootstrapped "
    "by default)",
)
@click.option(
    "--prompt", help="Provides an alternative prompt prefix for " "this environment."
)
@click.option(
    "--upgrade-deps",
    is_flag=True,
    help=f'Upgrade core dependencies ({", ".join(CORE_VENV_DEPS)}) '
    "to the latest version in PyPI",
)
@click.option(
    "--without-scm-ignore-files",
    "scm_ignore_files",
    is_flag=True,
    flag_value=False,
    default=True,
    help="Skips adding SCM ignore files to the environment "
    "directory (Git is supported by default).",
)
def cli(
    dirs,
    system_site,
    symlinks,
    copies,
    clear,
    upgrade,
    with_pip,
    prompt,
    upgrade_deps,
    scm_ignore_files,
):
    if upgrade and clear:
        raise ValueError("you cannot supply --upgrade and --clear together.")

    if copies:
        # print(f"{copies = }")
        symlinks = False
    # print(f"{dir = }, {system_site = }, {symlinks = }, {clear = }, {upgrade = }, {with_pip = }, {prompt = }, {upgrade_deps = }")
    # defaults: dir = '.asdf', system_site = False, symlinks = False,
    # clear = False, upgrade = False, with_pip = True, prompt = None, upgrade_deps = False
    for d in dirs:
        py_venv_builder = EnvBuilder(
            system_site_packages=system_site,
            clear=clear,
            symlinks=symlinks,
            upgrade=upgrade,
            with_pip=with_pip,
            prompt=prompt,
            upgrade_deps=upgrade_deps,
        )

        py_venv_builder.create(d)

        # print(f"{scm_ignore_files = }")
        # if isinstance(scm_ignore_files, str):
        #     scm_ignore_files = eval(scm_ignore_files)
        mojo_venv_builder = MojoEnvBuilder(
            system_site_packages=system_site,
            clear=clear,
            symlinks=symlinks,
            upgrade=upgrade,
            prompt=prompt,
            upgrade_deps=upgrade_deps,
            scm_ignore_files=scm_ignore_files,
        )
        mojo_venv_builder.create(d)


--- src/menv/scripts/common/Activate.ps1 ---

<#
.Synopsis
Activate a Python virtual environment for the current PowerShell session.

.Description
Pushes the python executable for a virtual environment to the front of the
$Env:PATH environment variable and sets the prompt to signify that you are
in a Python virtual environment. Makes use of the command line switches as
well as the `pyvenv.cfg` file values present in the virtual environment.

.Parameter VenvDir
Path to the directory that contains the virtual environment to activate. The
default value for this is the parent of the directory that the Activate.ps1
script is located within.

.Parameter Prompt
The prompt prefix to display when this virtual environment is activated. By
default, this prompt is the name of the virtual environment folder (VenvDir)
surrounded by parentheses and followed by a single space (ie. '(.venv) ').

.Example
Activate.ps1
Activates the Python virtual environment that contains the Activate.ps1 script.

.Example
Activate.ps1 -Verbose
Activates the Python virtual environment that contains the Activate.ps1 script,
and shows extra information about the activation as it executes.

.Example
Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
Activates the Python virtual environment located in the specified location.

.Example
Activate.ps1 -Prompt "MyPython"
Activates the Python virtual environment that contains the Activate.ps1 script,
and prefixes the current prompt with the specified string (surrounded in
parentheses) while the virtual environment is active.

.Notes
On Windows, it may be required to enable this Activate.ps1 script by setting the
execution policy for the user. You can do this by issuing the following PowerShell
command:

PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

For more information on Execution Policies: 
https://go.microsoft.com/fwlink/?LinkID=135170

#>
Param(
    [Parameter(Mandatory = $false)]
    [String]
    $VenvDir,
    [Parameter(Mandatory = $false)]
    [String]
    $Prompt
)

<# Function declarations --------------------------------------------------- #>

<#
.Synopsis
Remove all shell session elements added by the Activate script, including the
addition of the virtual environment's Python executable from the beginning of
the PATH variable.

.Parameter NonDestructive
If present, do not remove this function from the global namespace for the
session.

#>
function global:deactivate ([switch]$NonDestructive) {
    # Revert to original values

    # The prior prompt:
    if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
        Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
        Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
    }

    # The prior PYTHONHOME:
    if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
        Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
        Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
    }

    # The prior PATH:
    if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
        Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
        Remove-Item -Path Env:_OLD_VIRTUAL_PATH
    }

    # Just remove the VIRTUAL_ENV altogether:
    if (Test-Path -Path Env:VIRTUAL_ENV) {
        Remove-Item -Path env:VIRTUAL_ENV
    }

    # Just remove VIRTUAL_ENV_PROMPT altogether.
    if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
        Remove-Item -Path env:VIRTUAL_ENV_PROMPT
    }

    # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
    if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
        Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
    }

    # Leave deactivate function in the global namespace if requested:
    if (-not $NonDestructive) {
        Remove-Item -Path function:deactivate
    }
}

<#
.Description
Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
given folder, and returns them in a map.

For each line in the pyvenv.cfg file, if that line can be parsed into exactly
two strings separated by `=` (with any amount of whitespace surrounding the =)
then it is considered a `key = value` line. The left hand string is the key,
the right hand is the value.

If the value starts with a `'` or a `"` then the first and last character is
stripped from the value before being captured.

.Parameter ConfigDir
Path to the directory that contains the `pyvenv.cfg` file.
#>
function Get-PyVenvConfig(
    [String]
    $ConfigDir
) {
    Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"

    # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
    $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue

    # An empty map will be returned if no config file is found.
    $pyvenvConfig = @{ }

    if ($pyvenvConfigPath) {

        Write-Verbose "File exists, parse `key = value` lines"
        $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath

        $pyvenvConfigContent | ForEach-Object {
            $keyval = $PSItem -split "\s*=\s*", 2
            if ($keyval[0] -and $keyval[1]) {
                $val = $keyval[1]

                # Remove extraneous quotations around a string value.
                if ("'""".Contains($val.Substring(0, 1))) {
                    $val = $val.Substring(1, $val.Length - 2)
                }

                $pyvenvConfig[$keyval[0]] = $val
                Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
            }
        }
    }
    return $pyvenvConfig
}


<# Begin Activate script --------------------------------------------------- #>

# Determine the containing directory of this script
$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
$VenvExecDir = Get-Item -Path $VenvExecPath

Write-Verbose "Activation script is located in path: '$VenvExecPath'"
Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"

# Set values required in priority: CmdLine, ConfigFile, Default
# First, get the location of the virtual environment, it might not be
# VenvExecDir if specified on the command line.
if ($VenvDir) {
    Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
}
else {
    Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
    $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
    Write-Verbose "VenvDir=$VenvDir"
}

# Next, read the `pyvenv.cfg` file to determine any required value such
# as `prompt`.
$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir

# Next, set the prompt from the command line, or the config file, or
# just use the name of the virtual environment folder.
if ($Prompt) {
    Write-Verbose "Prompt specified as argument, using '$Prompt'"
}
else {
    Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
    if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
        Write-Verbose "  Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
        $Prompt = $pyvenvCfg['prompt'];
    }
    else {
        Write-Verbose "  Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
        Write-Verbose "  Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
        $Prompt = Split-Path -Path $venvDir -Leaf
    }
}

Write-Verbose "Prompt = '$Prompt'"
Write-Verbose "VenvDir='$VenvDir'"

# Deactivate any currently active virtual environment, but leave the
# deactivate function in place.
deactivate -nondestructive

# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
# that there is an activated venv.
$env:VIRTUAL_ENV = $VenvDir

if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {

    Write-Verbose "Setting prompt to '$Prompt'"

    # Set the prompt to include the env name
    # Make sure _OLD_VIRTUAL_PROMPT is global
    function global:_OLD_VIRTUAL_PROMPT { "" }
    Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
    New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt

    function global:prompt {
        Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
        _OLD_VIRTUAL_PROMPT
    }
    $env:VIRTUAL_ENV_PROMPT = $Prompt
}

# Clear PYTHONHOME
if (Test-Path -Path Env:PYTHONHOME) {
    Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
    Remove-Item -Path Env:PYTHONHOME
}

# Add the venv to the PATH
Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"


--- src/menv/scripts/common/activate ---

# This file must be used with "source bin/activate" *from bash*
# you cannot run it directly

deactivate () {
    # reset old environment variables
    if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
        PATH="${_OLD_VIRTUAL_PATH:-}"
        export PATH
        unset _OLD_VIRTUAL_PATH
    fi
    if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
        PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
        export PYTHONHOME
        unset _OLD_VIRTUAL_PYTHONHOME
    fi

    # This should detect bash and zsh, which have a hash command that must
    # be called to get it to forget past commands.  Without forgetting
    # past commands the $PATH changes we made may not be respected
    if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
        hash -r 2> /dev/null
    fi

    if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
        PS1="${_OLD_VIRTUAL_PS1:-}"
        export PS1
        unset _OLD_VIRTUAL_PS1
    fi

    unset VIRTUAL_ENV
    unset VIRTUAL_ENV_PROMPT
    if [ ! "${1:-}" = "nondestructive" ] ; then
    # Self destruct!
        unset -f deactivate
    fi
}

# unset irrelevant variables
deactivate nondestructive

VIRTUAL_ENV="__VENV_DIR__"
export VIRTUAL_ENV

_OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH"
export PATH

# unset PYTHONHOME if set
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
# could use `if (set -u; : $PYTHONHOME) ;` in bash
if [ -n "${PYTHONHOME:-}" ] ; then
    _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
    unset PYTHONHOME
fi

if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
    _OLD_VIRTUAL_PS1="${PS1:-}"
    PS1="__VENV_PROMPT__${PS1:-}"
    export PS1
    VIRTUAL_ENV_PROMPT="__VENV_PROMPT__"
    export VIRTUAL_ENV_PROMPT
fi

# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands.  Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
    hash -r 2> /dev/null
fi


--- src/menv/scripts/common/mactivate ---

# This file must be used with "source bin/activate" *from bash*
# you cannot run it directly
ORIGINAL_DIR="$(pwd)"
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"  # get the directory of this script
source "${SCRIPT_DIR}/activate"

if [ $? -eq 0 ]; then
    true  # echo "Activate successfully"
else
    deactivate () {
        # reset old environment variables
        if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
            PATH="${_OLD_VIRTUAL_PATH:-}"
            export PATH
            unset _OLD_VIRTUAL_PATH
        fi
        if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
            PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
            export PYTHONHOME
            unset _OLD_VIRTUAL_PYTHONHOME
        fi

        # This should detect bash and zsh, which have a hash command that must
        # be called to get it to forget past commands.  Without forgetting
        # past commands the $PATH changes we made may not be respected
        if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
            hash -r 2> /dev/null
        fi

        if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
            PS1="${_OLD_VIRTUAL_PS1:-}"
            export PS1
            unset _OLD_VIRTUAL_PS1
        fi

        unset VIRTUAL_ENV
        unset VIRTUAL_ENV_PROMPT
        if [ ! "${1:-}" = "nondestructive" ] ; then
        # Self destruct!
            unset -f deactivate
        fi
    }


    # unset irrelevant variables
    deactivate nondestructive

    VIRTUAL_ENV="__VENV_DIR__"
    export VIRTUAL_ENV

    _OLD_VIRTUAL_PATH="$PATH"
    PATH="$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH"
    export PATH

    # unset PYTHONHOME if set
    # this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
    # could use `if (set -u; : $PYTHONHOME) ;` in bash
    if [ -n "${PYTHONHOME:-}" ] ; then
        _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
        unset PYTHONHOME
    fi

    if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
        _OLD_VIRTUAL_PS1="${PS1:-}"
        PS1="__VENV_PROMPT__${PS1:-}"
        export PS1
        VIRTUAL_ENV_PROMPT="__VENV_PROMPT__"
        export VIRTUAL_ENV_PROMPT
    fi

    # This should detect bash and zsh, which have a hash command that must
    # be called to get it to forget past commands.  Without forgetting
    # past commands the $PATH changes we made may not be respected
    if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
        hash -r 2> /dev/null
    fi
fi

PATH="__VENV_BIN_PATH__:$PATH"
export PATH
cd "$ORIGINAL_DIR"

--- src/menv/scripts/nt/activate.bat ---

@echo off

rem This file is UTF-8 encoded, so we need to update the current code page while executing it
for /f "tokens=2 delims=:." %%a in ('"%SystemRoot%\System32\chcp.com"') do (
    set _OLD_CODEPAGE=%%a
)
if defined _OLD_CODEPAGE (
    "%SystemRoot%\System32\chcp.com" 65001 > nul
)

set VIRTUAL_ENV=__VENV_DIR__

if not defined PROMPT set PROMPT=$P$G

if defined _OLD_VIRTUAL_PROMPT set PROMPT=%_OLD_VIRTUAL_PROMPT%
if defined _OLD_VIRTUAL_PYTHONHOME set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%

set _OLD_VIRTUAL_PROMPT=%PROMPT%
set PROMPT=__VENV_PROMPT__%PROMPT%

if defined PYTHONHOME set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME%
set PYTHONHOME=

if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH%
if not defined _OLD_VIRTUAL_PATH set _OLD_VIRTUAL_PATH=%PATH%

set PATH=%VIRTUAL_ENV%\__VENV_BIN_NAME__;%PATH%
set VIRTUAL_ENV_PROMPT=__VENV_PROMPT__

:END
if defined _OLD_CODEPAGE (
    "%SystemRoot%\System32\chcp.com" %_OLD_CODEPAGE% > nul
    set _OLD_CODEPAGE=
)


--- src/menv/scripts/nt/deactivate.bat ---

@echo off

if defined _OLD_VIRTUAL_PROMPT (
    set "PROMPT=%_OLD_VIRTUAL_PROMPT%"
)
set _OLD_VIRTUAL_PROMPT=

if defined _OLD_VIRTUAL_PYTHONHOME (
    set "PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%"
    set _OLD_VIRTUAL_PYTHONHOME=
)

if defined _OLD_VIRTUAL_PATH (
    set "PATH=%_OLD_VIRTUAL_PATH%"
)

set _OLD_VIRTUAL_PATH=

set VIRTUAL_ENV=
set VIRTUAL_ENV_PROMPT=

:END


--- src/menv/scripts/posix/activate.csh ---

# This file must be used with "source bin/activate.csh" *from csh*.
# You cannot run it directly.
# Created by Davide Di Blasi <davidedb@gmail.com>.
# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>

alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'

# Unset irrelevant variables.
deactivate nondestructive

setenv VIRTUAL_ENV "__VENV_DIR__"

set _OLD_VIRTUAL_PATH="$PATH"
setenv PATH "$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH"


set _OLD_VIRTUAL_PROMPT="$prompt"

if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
    set prompt = "__VENV_PROMPT__$prompt"
    setenv VIRTUAL_ENV_PROMPT "__VENV_PROMPT__"
endif

alias pydoc python -m pydoc

rehash


--- src/menv/scripts/posix/activate.fish ---

# This file must be used with "source <venv>/bin/activate.fish" *from fish*
# (https://fishshell.com/); you cannot run it directly.

function deactivate  -d "Exit virtual environment and return to normal shell environment"
    # reset old environment variables
    if test -n "$_OLD_VIRTUAL_PATH"
        set -gx PATH $_OLD_VIRTUAL_PATH
        set -e _OLD_VIRTUAL_PATH
    end
    if test -n "$_OLD_VIRTUAL_PYTHONHOME"
        set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
        set -e _OLD_VIRTUAL_PYTHONHOME
    end

    if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
        set -e _OLD_FISH_PROMPT_OVERRIDE
        # prevents error when using nested fish instances (Issue #93858)
        if functions -q _old_fish_prompt
            functions -e fish_prompt
            functions -c _old_fish_prompt fish_prompt
            functions -e _old_fish_prompt
        end
    end

    set -e VIRTUAL_ENV
    set -e VIRTUAL_ENV_PROMPT
    if test "$argv[1]" != "nondestructive"
        # Self-destruct!
        functions -e deactivate
    end
end

# Unset irrelevant variables.
deactivate nondestructive

set -gx VIRTUAL_ENV "__VENV_DIR__"

set -gx _OLD_VIRTUAL_PATH $PATH
set -gx PATH "$VIRTUAL_ENV/__VENV_BIN_NAME__" $PATH

# Unset PYTHONHOME if set.
if set -q PYTHONHOME
    set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
    set -e PYTHONHOME
end

if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
    # fish uses a function instead of an env var to generate the prompt.

    # Save the current fish_prompt function as the function _old_fish_prompt.
    functions -c fish_prompt _old_fish_prompt

    # With the original prompt function renamed, we can override with our own.
    function fish_prompt
        # Save the return status of the last command.
        set -l old_status $status

        # Output the venv prompt; color taken from the blue of the Python logo.
        printf "%s%s%s" (set_color 4B8BBE) "__VENV_PROMPT__" (set_color normal)

        # Restore the return status of the previous command.
        echo "exit $old_status" | .
        # Output the original/"old" prompt.
        _old_fish_prompt
    end

    set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
    set -gx VIRTUAL_ENV_PROMPT "__VENV_PROMPT__"
end


--- src/menv/utils.py ---

CORE_VENV_DEPS = ("pip",)  # TODO: assure


--- tests/__init__.py ---



--- tests/unit/__init__.py ---



--- tests/unit/test_builder.py ---

import shutil
from pathlib import Path
from pprint import pprint

import pytest

from menv.builder import (
    MojoEnvBuilder,
    change_config,
    clear_directory,
    create_if_needed,
)


class TestMojoEnvBuilder:
    @classmethod
    def setup_class(cls):
        cls.builder = MojoEnvBuilder()
        cls.venv_path = Path(__file__).parent / "venv"
        cls.context = cls.builder.ensure_directories(cls.venv_path)

    def test_init(self) -> None:
        pprint(f"{self.context = }")  # use pytest -s to show this print
        pass

    def test_ensure_directories(self) -> None:
        assert hasattr(self.context, "env_dir")
        assert hasattr(self.context, "env_name")
        assert hasattr(self.context, "prompt")
        assert hasattr(self.context, "mojo_dir")
        assert hasattr(self.context, "mojo_exe")
        assert hasattr(self.context, "executable")
        assert hasattr(self.context, "bin_name")
        assert hasattr(self.context, "pkg_dir")
        assert hasattr(self.context, "bin_path")
        assert hasattr(self.context, "lib_path")
        assert hasattr(self.context, "env_exe")
        assert hasattr(self.context, "env_cfg")

    def test_install_scripts(self):
        self.builder.setup_scripts(self.context)

        assert Path(self.context.bin_path).joinpath("mactivate").exists()

    # comment to debug venv
    @classmethod
    def teardown_class(cls) -> None:
        shutil.rmtree(cls.venv_path)



--- .gitignore ---

# Ignore Visual Studio Code settings folder
.vscode

# Ignore Python bytecode cache files
__pycache__

# Ignore egg-info directories generated by setuptools
*.egg-info/

# Ignore Jupyter Notebook checkpoints
.ipynb_checkpoints

# Ignore all Jupyter notebooks
*.ipynb

# Except for the Jupyter notebooks specified below
!benchmarks/*.ipynb

# Ignore the build directory
build/

# Ignore Mojo packages in the benchmarks directory
benchmarks/*.mojopkg


--- ACKNOWLEDGEMENTS.md ---

# Acknowledgements

Portions of Specials package may utilize the following copyrighted material, the use of which is hereby acknowledged.

---

Summary:

* [LLVM Project](#llvm-project)
* [Mojo](#mojo)
* [NumPy](#numpy)
* [TensorFlow Probability](#tensorflow-probability)

---

### [LLVM Project](https://github.com/modularml/mojo/)

    ==============================================================================
    The LLVM Project is 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.
    
    ==============================================================================
    Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy):
    ==============================================================================
    University of Illinois/NCSA
    Open Source License
    
    Copyright (c) 2003-2019 University of Illinois at Urbana-Champaign.
    All rights reserved.
    
    Developed by:
    
        LLVM Team
    
        University of Illinois at Urbana-Champaign
    
        http://llvm.org
    
    Permission is hereby granted, free of charge, to any person obtaining a copy of
    this software and associated documentation files (the "Software"), to deal with
    the Software without restriction, including without limitation the rights to
    use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
    of the Software, and to permit persons to whom the Software is furnished to do
    so, subject to the following conditions:
    
        * Redistributions of source code must retain the above copyright notice,
          this list of conditions and the following disclaimers.
    
        * Redistributions in binary form must reproduce the above copyright notice,
          this list of conditions and the following disclaimers in the
          documentation and/or other materials provided with the distribution.
    
        * Neither the names of the LLVM Team, University of Illinois at
          Urbana-Champaign, nor the names of its contributors may be used to
          endorse or promote products derived from this Software without specific
          prior written permission.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
    FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
    SOFTWARE.

### [Mojo](https://github.com/modularml/mojo/)

    ===================================================================================
    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.

### [NumPy](https://github.com/numpy/numpy)

    Copyright (c) 2005-2023, NumPy Developers.
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are
    met:

        * Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.

        * Redistributions in binary form must reproduce the above
        copyright notice, this list of conditions and the following
        disclaimer in the documentation and/or other materials provided
        with the distribution.

        * Neither the name of the NumPy Developers nor the names of any
        contributors may be used to endorse or promote products derived
        from this software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

### [TensorFlow Probability](https://github.com/tensorflow/probability)

    Copyright 2018 The TensorFlow Probability Authors. All rights reserved.

                                  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 2018, The TensorFlow Probability Authors.

    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.

    ------------------------------------------------------------------------

    A file in TensorFlow Probability is also covered by the following license and
    copyright.

    Files:
    - tensorflow_probability/python/distributions/pixel_cnn.py

    Copyright 2019 OpenAI (http://openai.com)

                                    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 2019 OpenAI (http://openai.com)

    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.

--- LICENSE ---

                                 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.


--- README.md ---

> [!WARNING]
> Specials is under development and the API is subject to change.

# Welcome to Specials

Specials is a [Mojo](https://www.modular.com/mojo) package designed to provide highly-optimized and hardware-acceleration-friendly [special functions](https://en.wikipedia.org/wiki/Special_functions) implementations for AI computing.

Mojo combines the usability of Python with the performance of C, unlocking unparalleled programmability of AI hardware and extensibility of AI models. This makes Mojo an ideal choice for implementing special functions that require not only numerical accuracy and stability but also high performance.

## Table of Contents

- [Special Functions in AI](#special-functions-in-ai)
- [Why Mojo 🔥 for Specials?](#why-mojo--for-specials)
- [Why the Focus on Special Functions?](#why-the-focus-on-special-functions)
- [Mojo Version Requirement](#mojo-version-requirement)
- [Getting Started](#getting-started)
- [Example Usage](#example-usage)
- [Benchmarks](#benchmarks)
- [Some Implementations Available](#some-implementations-available)
  * [Elementary Functions](#elementary-functions)
- [Contributing](#contributing)
- [References](#references)

## Special Functions in AI

Special functions are integral to various AI applications. For instance:

- The Gaussian Error Linear Unit (GELU) [[1](#hendrycks2016)], a high-performing neural network activation function, is based on the [Gauss error](https://en.wikipedia.org/wiki/Error_function) function.

- Numerical methods for [Bessel](https://en.wikipedia.org/wiki/Bessel_function), [incomplete beta](https://en.wikipedia.org/wiki/Beta_function#Incomplete_beta_function), and [incomplete gamma](https://en.wikipedia.org/wiki/Incomplete_gamma_function) functions enable implicit differentiation [[2](#figurnov2018)] of cumulative distribution functions, facilitating the training of probabilistic models with von Mises, gamma, and beta latent variables.

Recognizing the relevance of special functions in the field, major AI frameworks provide implementations for many of them. Both PyTorch and JAX include dedicated modules, namely [`torch.special`](https://pytorch.org/docs/stable/special.html) and [`jax.scipy.special`](https://jax.readthedocs.io/en/latest/jax.scipy.html#module-jax.scipy.special). TensorFlow and TensorFlow Probability incorporate them into their mathematical functions APIs, accessible through [`tf.math`](https://www.tensorflow.org/api_docs/python/tf/math) and [`tfp.math`](https://www.tensorflow.org/probability/api_docs/python/tfp/math).

## Why Mojo 🔥 for Specials?

By adopting the Mojo programming language, Specials enhances the implementation of special functions with several key advantages:

- **Simplified Complexity with High Performance.** Mojo combines Python's simplicity with C-level performance, eliminating the need for wrapping low-level language code.

- **Support for Hardware Accelerators.** Mojo is designed to leverage the power of GPUs, TPUs, and other AI hardware without the need for C++ or CUDA.

- **Highly Accurate and Optimized Implementations.** Mojo enables the implementation of state-of-the-art numerical methods, ensuring numerical accuracy and stability as well as high performance.

## Why the Focus on Special Functions?

Special functions are particular mathematical functions that play a fundamental role in various scientific and industrial disciplines, about which many useful properties are known. They find extensive applications in physics, engineering, chemistry, computer science, and statistics, being prized for their ability to provide closed-form solutions to complex problems in these fields.

## Mojo Version Requirement

Specials requires Mojo `24.4.0`. Make sure you have the correct Mojo version installed before using this package.

## Getting Started

To get started, access a Mojo programming environment directly via the setup instructions on the Mojo [installation page](https://docs.modular.com/mojo/manual/get-started/).

Clone the Specials repository to your machine:

```bash
git clone https://github.com/leandrolcampos/specials.git
```

Considering that Mojo SDK as well as our benchmarks and tests depend on an existing installed version of Python, follow the instructions below to create and activate a Python virtual environment with Conda:

1. Install Conda by following the 
   [Quick command-line install instructions](https://docs.conda.io/projects/miniconda/en/latest/#quick-command-line-install). Ensure Conda is initialized for your shell:

   ```bash
   ~/miniconda3/bin/conda init zsh
   # or
   ~/miniconda3/bin/conda init bash
   ```

2. Restart your shell.

3. Navigate to the cloned Specials repository and run the following commands to create and activate a Conda environment named `specials`:

   ```bash
   conda env create -f python_environment.yml
   conda activate specials
   ```

**Optional:** If using Visual Studio Code, consider adding the following items to `mojo.lsp.includeDirs` setting in the user or remote scope:

- `/path/to/repo/src`
- `/path/to/repo/test`

Replace `/path/to/repo` with the absolute path of the cloned Specials repository.

## Example Usage

The following code snippet shows how to compute `exp(x) - 1` in a numerically stable way for a given SIMD vector:

```python
>>> import specials
>>> var x = SIMD[DType.float64, 4](0.0, 1e-18, 0.2, 1.0)
>>> var result = specials.expm1(x)
>>> print(result)
[0.0, 1.0000000000000001e-18, 0.22140275816016985, 1.7182818284590453]
```

## Benchmarks

The [`benchmarks`](./benchmarks/) directory contains a collection of Mojo notebooks designed to compare the accuracy and runtime performance of functions from the Specials package against those found in well-known Mojo and Python packages.

These benchmarks aim to highlight the correctness and efficiency of Specials implementations.

Please note that for us, **Accuracy > Performance**: when forced to choose between FLOPS and numerical accuracy, we always prefer numerical accuracy.

## Some Implementations Available

### Elementary Functions

| Function | Description |
|----------|-------------|
| `exp(x)` | The exponential function |
| `exp2(x)` | The base-2 exponential function |
| `expm1(x)` | The expression `exp(x) - 1` evaluated in a numerically stable way when `x` is near zero |
| `log(x)` | The logarithm function |
| `log1p(x)` | The expression `log(1 + x)` evaluated in a numerically stable way when `x` is near zero |

## Contributing

We are not accepting pull requests at this time. However, you can contribute by reporting issues or suggesting features through the creation of a GitHub issue [here](https://github.com/leandrolcampos/specials/issues).

## References

[<a id="hendrycks2016">1</a>]
Hendrycks, Dan, and Kevin Gimpel. "Gaussian error linear units (gelus)." _arXiv preprint arXiv:1606.08415_ (2016). [[Link](https://arxiv.org/abs/1606.08415)]

[<a id="figurnov2018">2</a>]
Figurnov, Mikhail, Shakir Mohamed, and Andriy Mnih. "Implicit reparameterization gradients." _Advances in neural information processing systems_ 31 (2018). [[Link](https://arxiv.org/abs/1805.08498)]


--- ROADMAP.md ---

# Roadmap

## Introduction

This document outlines the development roadmap for Specials, detailing our vision, current focus, and future goals. This roadmap is intended to provide guidance for contributors and users about the future direction of the project.

## Vision

The Specials package aims to provide highly-optimized, hardware-accelerated special functions implemented in Mojo, with a strong emphasis on numerical accuracy and stability. Our goal is to become a go-to resource for special functions in AI computing.

## Current Focus (Next 12 Months)

- **Keep Up with Mojo Updates**: Monitor updates to the Mojo compiler and standard library, ensuring the proper use of language features that best meet the needs of Specials.
- **Adopt Language Standards and Best Practices**: Follow Mojo's standards and best practices for code formatting and documentation.
- **Explore Numerical Methods**: Investigate numerical methods to generate a single polynomial approximation that produces correctly rounded results for all inputs of a given elementary function, regardless of the floating-point format used. This will facilitate the addition of support for new formats and reduce the number of lookup tables.
- **Review Critical Algorithms and Data Structures**: Optimize critical algorithms and data structures used in various mathematical function implementations to reduce execution time, memory usage, and package size.
- **Facilitate Benchmarking**: Make it easier to add and maintain micro benchmarks for Specials, promoting reliability and reproducibility of comparative accuracy and computational efficiency results. Ensure benchmarks are fair, meaning they evaluate alternatives under the same conditions.
- **Expand and Rigorize Testing**: Enhance test coverage for numerical methods and data structures, floating-point formats, input domains, and edge cases. Make tests more rigorous by reducing tolerance values appropriately for the precision of each floating-point format.
- **Ensure Hardware Accelerator Support**: Ensure Specials executes correctly and efficiently on hardware accelerators supported by Mojo.

For the next few months, adding implementations for new mathematical functions is not a priority. The rapid evolution of Mojo makes adapting a large codebase costly. Possible exceptions include the `gamma`, `lgamma`, `beta`, `lbeta` functions, and the elementary functions they depend on.

## Future Goals (Beyond 12 Months)

- **Add Implementations for Special Functions**:
  - `igamma`: Regularized incomplete Gamma function
  - `igammac`: Complement of the regularized incomplete Gamma function
  - `igammainv`: Inverse of the `igamma` function
  - `igammacinv`: Inverse of the `igammac` function
  - `ibeta`: Regularized incomplete Beta function
  - `ibetac`: Complement of the regularized incomplete Beta function
  - `ibetainv`: Inverse of the `ibeta` function
  - `ibetacinv`: Inverse of the `ibetainv` function
  - `erf`: Error function
  - `erfc`: Complement of the error function
  - `erfinv`: Inverse of the `erf` function
  - `erfcinv`: Inverse of the `erfc` function
  - `erfcx`: Scaled complementary error function

- **Explore Partial Derivatives**: Investigate ways to implement partial derivatives of the special functions listed above.

- **Start Accepting Community Contributions**: Begin accepting pull requests with contributions from the community to foster collaboration and enhance the development of Specials.


--- benchmarks/README.md ---

# Benchmarks

This directory contains a collection of Mojo notebooks designed to compare the accuracy and runtime performance of functions from the Specials package against those found in well-known Mojo and Python packages.

The benchmarks aim to highlight the correctness and efficiency of our implementations.

Please note that for us, **Accuracy > Performance**: when forced to choose between FLOPS and numerical accuracy, we always prefer numerical accuracy.

## Getting Started

Before running the benchmarks, ensure you have completed the initial setup for the project. This includes installing Mojo, cloning the Specials repository, and setting up and activating a Python environment with Conda. Detailed setup instructions are available [here](../README.md#getting-started).

Once the initial setup is complete, build the Specials package to ensure all components are ready for benchmarking. Run the following command in the `specials/benchmarks` directory of the cloned repository:

```bash
../scripts/prepare-benchmarks.sh
```

## Running

The benchmarks can be run in [Visual Studio Code](https://github.com/modularml/mojo/tree/main/examples/notebooks#get-started-in-vs-code) or [JupyterLab](https://github.com/modularml/mojo/tree/main/examples/notebooks#get-started-with-jupyterlab) with Mojo kernel support. Follow the respective guides to set up your environment.

Once set up, open the desired notebook in the `specials/benchmarks` directory to begin running the benchmarks.


--- python_environment.yml ---

name: specials
channels:
  - conda-forge
  - defaults
dependencies:
  - mpmath
  - numpy
  - pip
  - pytest
  - python=3.11
  - scipy
  - pip:
      - lit
      - tabulate


--- scripts/README.md ---

# Scripts

This directory contains scripts to build, prepare for benchmarks, and test the Specials package.

## Overview

- `build-specials.sh`: This script packages the Specials for release into the `build` directory.
- `prepare-benchmarks.sh`: This script prepares the benchmark environment by installing the needed Python dependencies and packaging the Specials and the `test_utils` separately into the `benchmarks` directory.
- `run-tests.sh`: This script prepares the test environment according to the specified build mode (`debug` or `release`) by installing the needed Python dependencies, packaging the Specials and the `test_utils` separately into the `build` directory, and running the tests.

The `test_utils` package contains utilities designed exclusively for internal use in benchmarks and testing of the Specials code. It is not intended for external use and should not be relied upon as part of the public API or used in production environments.

## Getting Started

Before using these scripts, ensure you have completed the initial setup for the project. This includes installing Mojo, cloning the Specials repository, and setting up and activating a Python environment with Conda. Detailed setup instructions are available [here](../README.md#getting-started).

Following these steps will prepare the necessary environment to effectively build, benchmark, and test the Specials.

## Usage

### Building the Specials

To package the Specials, navigate to the repository root and run:

```bash
./scripts/build-specials.sh
```

This command compiles the Specials into a `.mojopkg` package suitable for release, and places it in the `build` directory.

### Preparing for Benchmarks

If you are setting up for benchmarks, use:

```bash
./scripts/prepare-benchmarks.sh
```

This command prepares the benchmark environment by installing the needed Python dependencies and packaging the Specials and the `test_utils` separately into the `benchmarks` directory.

### Running Tests

To run the tests, specify the build mode as either `debug` or `release`. By default, the script uses the `debug` mode:

```bash
./scripts/run-tests.sh
```

In the `debug` mode, the script sets the `debug-level` to `full` and defines `MOJO_ENABLE_ASSERTIONS`, providing detailed debugging output and enabling runtime assertions for thorough testing.

Alternatively, you can run the tests in the `release` build mode:

```bash
./scripts/run-tests.sh release
```

This command runs the tests in the `release` mode, optimizing performance by setting the `debug-level` to `none` and not defining `MOJO_ENABLE_ASSERTIONS`. This mode ensures that the build is optimized and stable for production use.


--- scripts/build-specials.sh ---

#!/usr/bin/env bash
##===----------------------------------------------------------------------===##
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
##===----------------------------------------------------------------------===##

set -euo pipefail

SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
REPO_ROOT=$(realpath "${SCRIPT_DIR}/..")
BUILD_DIR="${REPO_ROOT}/build"
mkdir -p "${BUILD_DIR}"

echo "Packaging up the Specials"
SPECIALS_PATH="${REPO_ROOT}/src/specials"
FULL_SPECIALS_PACKAGE_PATH="${BUILD_DIR}/specials.mojopkg"
mojo package "${SPECIALS_PATH}" -o "${FULL_SPECIALS_PACKAGE_PATH}"

echo Successfully created "${FULL_SPECIALS_PACKAGE_PATH}"


--- scripts/prepare-benchmarks.sh ---

#!/usr/bin/env bash
##===----------------------------------------------------------------------===##
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
##===----------------------------------------------------------------------===##

set -euo pipefail

SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
REPO_ROOT=$(realpath "${SCRIPT_DIR}/..")
BUILD_DIR="${REPO_ROOT}/benchmarks"
mkdir -p "${BUILD_DIR}"

echo "Packaging up the Specials"
SPECIALS_PATH="${REPO_ROOT}/src/specials"
FULL_SPECIALS_PACKAGE_PATH="${BUILD_DIR}/specials.mojopkg"
mojo package "${SPECIALS_PATH}" -o "${FULL_SPECIALS_PACKAGE_PATH}"

echo Successfully created "${FULL_SPECIALS_PACKAGE_PATH}"

echo "Installing the Python dependencies of test_utils"
python3 -m pip install -q -e "${REPO_ROOT}"/test

echo "Packaging up the test_utils"
TEST_UTILS_PATH="${REPO_ROOT}/test/test_utils"
FULL_TEST_UTILS_PACKAGE_PATH="${BUILD_DIR}/test_utils.mojopkg"
mojo package "${TEST_UTILS_PATH}" \
             -I "${BUILD_DIR}" \
             -o "${FULL_TEST_UTILS_PACKAGE_PATH}"

echo Successfully created "${FULL_TEST_UTILS_PACKAGE_PATH}"


--- scripts/run-tests.sh ---

#!/usr/bin/env bash
##===----------------------------------------------------------------------===##
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
##===----------------------------------------------------------------------===##

set -euo pipefail

# Accept an optional build mode argument, default to 'debug'
build_mode="${1:-debug}"

# Validate the build mode
if [[ $build_mode != "debug" && $build_mode != "release" ]]; then
    echo "Error: Invalid build mode '$build_mode'"
    echo "Build mode must be either 'debug' or 'release'"
    exit 1
fi

export BUILD_MODE=$build_mode

SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
REPO_ROOT=$(realpath "${SCRIPT_DIR}/..")
BUILD_DIR="${REPO_ROOT}/build"
mkdir -p "${BUILD_DIR}"

echo "Packaging up the Specials"
SPECIALS_PATH="${REPO_ROOT}/src/specials"
FULL_SPECIALS_PACKAGE_PATH="${BUILD_DIR}/specials.mojopkg"
mojo package "${SPECIALS_PATH}" \
             -o "${FULL_SPECIALS_PACKAGE_PATH}"

echo Successfully created "${FULL_SPECIALS_PACKAGE_PATH}"

echo "Installing the Python dependencies of test_utils"
python3 -m pip install -q -e "${REPO_ROOT}"/test

echo "Packaging up the test_utils"
TEST_UTILS_PATH="${REPO_ROOT}/test/test_utils"
FULL_TEST_UTILS_PACKAGE_PATH="${BUILD_DIR}/test_utils.mojopkg"
mojo package "${TEST_UTILS_PATH}" \
             -I "${BUILD_DIR}" \
             -o "${FULL_TEST_UTILS_PACKAGE_PATH}"

echo Successfully created "${FULL_TEST_UTILS_PACKAGE_PATH}"

echo "Running tests for Specials"
lit -sv "${REPO_ROOT}"/test

# Clean up environment variable
unset BUILD_MODE


--- src/specials/__init__.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2023 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #

"""Specials"""

from .elementary.exp import exp
from .elementary.exp2 import exp2
from .elementary.expm1 import expm1
from .elementary.log import log
from .elementary.log1p import log1p
from .gamma import lbeta, lgamma_correction, lgamma1p, rgamma1pm1


--- src/specials/_internal/__init__.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2023 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #

"""Provides internal utilities and helpers for use within the package."""


--- src/specials/_internal/asserting.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2023 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #

"""
Implements utilities for performing compile-time assertions within the package.
"""

import bit
import math


@always_inline("nodebug")
fn assert_integral_dtype[
    parameter_name: StringLiteral, parameter_value: DType
]() -> None:
    """Asserts that the given parameter is an integral data type."""
    constrained[
        parameter_value.is_integral(),
        "The parameter `" + parameter_name + "` must be an integral data type.",
    ]()


@always_inline("nodebug")
fn assert_float_dtype[
    parameter_name: StringLiteral, parameter_value: DType
]() -> None:
    """Asserts that the given parameter is a floating-point data type.

    This package supports only floating-point data types of single (`float32`) or double
    (`float64`) precision.
    """
    constrained[
        parameter_value == DType.float32 or parameter_value == DType.float64,
        "The parameter `"
        + parameter_name
        + "` must be a floating-point of single (`float32`) or double"
        " (`float64`)"
        " precision.",
    ]()


@always_inline("nodebug")
fn assert_in_range[
    parameter_name: StringLiteral,
    parameter_value: Int,
    lower: Int,
    upper: Int,
]() -> None:
    """Asserts that the given parameter is within the specified range."""
    constrained[
        parameter_value >= lower and parameter_value < upper,
        "The parameter `"
        + parameter_name
        + "` must be within the specified range.",
    ]()


@always_inline("nodebug")
fn assert_non_zero[
    parameter_name: StringLiteral, parameter_value: Int
]() -> None:
    """Asserts the condition `parameter_value != 0` holds."""
    constrained[
        parameter_value != 0,
        "The parameter `" + parameter_name + "` must be non-zero.",
    ]()


@always_inline("nodebug")
fn assert_positive[
    parameter_name: StringLiteral, parameter_value: Int
]() -> None:
    """Asserts the condition `parameter_value > 0` holds."""
    constrained[
        parameter_value > 0,
        "The parameter `" + parameter_name + "` must be positive.",
    ]()


@always_inline("nodebug")
fn assert_positive[
    dtype: DType, parameter_name: StringLiteral, parameter_value: Scalar[dtype]
]() -> None:
    """Asserts the condition `parameter_value > 0` holds."""
    constrained[
        parameter_value > 0,
        "The parameter `" + parameter_name + "` must be positive.",
    ]()


@always_inline("nodebug")
fn assert_simd_width[
    parameter_name: StringLiteral, parameter_value: Int
]() -> None:
    """Asserts that the given parameter is a valid SIMD width."""
    constrained[
        parameter_value > 0 and bit.is_power_of_two(parameter_value),
        "The parameter `"
        + parameter_name
        + "` must be positive and a power of two.",
    ]()


--- src/specials/_internal/math.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #

"""Implements mathematical utilities for internal use."""

import math

from specials.utils.numerics import FloatLimits


fn ldexp[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width], exp: SIMD[DType.int32, simd_width]) -> SIMD[
    dtype,
    simd_width,
]:
    """Computes elementwise `ldexp` function.

    Compared to the standard library implementation, this implementation produces
    correct results when `exp` assumes values of greater magnitude.

    # TODO: Report this as a bug in the standard library.

    Parameters:
        dtype: 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 min_exponent: SIMD[DType.int32, simd_width] = FloatLimits[
        dtype
    ].min_exponent - 1
    alias max_exponent: SIMD[DType.int32, simd_width] = FloatLimits[
        dtype
    ].max_exponent - 1

    var result: SIMD[dtype, simd_width]

    if ((exp < min_exponent) | (exp > max_exponent)).reduce_or():
        var exponent_clipped = exp.clamp(min_exponent, max_exponent)
        var exponent_remainder = exp - exponent_clipped

        result = math.ldexp(math.ldexp(x, exponent_clipped), exponent_remainder)
    else:
        result = math.ldexp(x, exp)

    return result


--- src/specials/_internal/polynomial.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2023 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
#
# Some of the code in this file is adapted from:
#
# numpy/numpy
# Copyright (c) 2005-2023, NumPy Developers.
# Licensed under BSD 3 clause.
#
# slatec/fnlib (https://www.netlib.org/slatec/fnlib):
# Public-domain software. No copyright restrictions.

"""Implements utilities for efficiently working with polynomials."""

import math

from memory.unsafe import bitcast
from utils.static_tuple import StaticTuple

from specials._internal import asserting
from specials._internal.table import get_hexadecimal_dtype
from specials.utils.functional import fori_loop

# TODO: Consider using a trait when it supports defining default method implementations.


@always_inline("nodebug")
fn _check_polynomial_like_constraints[
    num_terms: Int, dtype: DType, simd_width: Int
]() -> None:
    """Checks the constraints of a polynomial-like."""
    asserting.assert_positive["num_terms", num_terms]()
    asserting.assert_float_dtype["dtype", dtype]()
    asserting.assert_simd_width["simd_width", simd_width]()


# ===-------------------------- Chebyshev Series --------------------------=== #


@register_passable("trivial")
struct Chebyshev[
    num_terms: Int,
    dtype: DType,
    simd_width: Int,
](Sized):
    """Represents a finite Chebyshev series.

    A Chebyshev series with `n + 1` terms is a linear combination of the form

    `p(x) = c[0] * T[0](x) + c[1] * T[1](x) + ... + c[n] * T[n](x)`

    where `x` is the independent variable defined in the interval `[-1, 1]`, `c[i]` is
    the `i`-th coefficient of the series, and `T[i]` is the `i`-th Chebyshev polynomial
    of the first kind.

    Parameters:
        num_terms: The number of terms in the series.
        dtype: The data type of the independent variable `x` in the series.
        simd_width: The SIMD width of the independent variable `x` in the series.

    Constraints:
        The number of terms must be positive. The parameter `dtype` must be `float32`
        or `float64`.
    """

    var _coefficients: StaticTuple[SIMD[dtype, simd_width], num_terms]

    @staticmethod
    fn from_coefficients[*coefficients: Scalar[dtype]]() -> Self:
        """Generates a Chebyshev series from a sequence of coefficients.

        Parameters:
            coefficients: The sequence of coefficients in order of increasing degree,
                i.e., `(1, 2, 3)` gives `1 * T[0](x) + 2 * T[1](x) + 3 * T[2](x)`.

        Returns:
            A Chebyshev series with the given coefficients.

        Constraints:
            The number of coefficients must be equal to the parameter `num_terms`.
        """
        _check_polynomial_like_constraints[num_terms, dtype, simd_width]()

        alias coefficients_list = VariadicList(coefficients)

        constrained[
            num_terms == len(coefficients_list),
            (
                "The number of coefficients must be equal to the parameter"
                " `num_terms`."
            ),
        ]()

        var splatted_coefficients = StaticTuple[
            SIMD[dtype, simd_width], num_terms
        ]()

        for i in range(num_terms):
            splatted_coefficients[i] = coefficients_list[i]

        return Self {_coefficients: splatted_coefficients}

    @staticmethod
    fn from_hexadecimal_coefficients[
        *coefficients: Scalar[get_hexadecimal_dtype[dtype]()]
    ]() -> Self:
        """Generates a Chebyshev series from a sequence of hexadecimal coefficients.

        The data type used for hexadecimal representation of the coefficients is
        automatically determined based on `dtype`: `uint32` if `dtype` is `float32`
        or `uint64` if `dtype` is `float64`.

        Parameters:
            coefficients: The sequence of hexadecimal coefficients in order of
                increasing degree. See the method `from_coefficients`.

        Returns:
            A Chebyshev series with the given hexadecimal coefficients.

        Constraints:
            The number of hexadecimal coefficients must be equal to the parameter
            `num_terms`.
        """
        _check_polynomial_like_constraints[num_terms, dtype, simd_width]()

        alias coefficients_list = VariadicList(coefficients)

        constrained[
            num_terms == len(coefficients_list),
            (
                "The number of hexadecimal coefficients must be equal to the"
                " parameter `num_terms`."
            ),
        ]()

        var splatted_coefficients = StaticTuple[
            SIMD[dtype, simd_width], num_terms
        ]()

        for i in range(num_terms):
            splatted_coefficients[i] = bitcast[dtype](coefficients_list[i])

        return Self {_coefficients: splatted_coefficients}

    @always_inline
    fn __len__(self: Self) -> Int:
        """Returns the number of terms in the Chebyshev series.

        This is known at compile time.

        Returns:
            The number of terms in the Chebyshev series.
        """
        return num_terms

    @always_inline
    fn degree(self: Self) -> Int:
        """Returns the degree of the Chebyshev series."""
        return num_terms - 1

    @always_inline
    fn get[index: Int](self: Self) -> SIMD[dtype, simd_width]:
        """Returns the coefficient of the Chebyshev series at the given index.

        Parameters:
            index: The index of the coefficient to return.

        Returns:
            A SIMD vector containing the coefficient of the Chebyshev series at
            the given index.

        Constraints:
            The index must be within the range `[0, num_terms)`.
        """
        asserting.assert_in_range["index", index, 0, num_terms]()
        return self._coefficients[index]

    fn truncate[
        num_terms: Int
    ](self: Self) -> Chebyshev[num_terms, dtype, simd_width]:
        """Truncates the Chebyshev series by discarding high-degree terms.

        Parameters:
            num_terms: The number of terms in the truncated series.

        Returns:
            A truncated Chebyshev series.

        Constraints:
            The number of terms in the truncated series must be positive and less than
            or equal to the number of terms in the original series.
        """
        asserting.assert_in_range[
            "num_terms", num_terms, 1, self.num_terms + 1
        ]()
        var coefficients = StaticTuple[SIMD[dtype, simd_width], num_terms]()

        @always_inline
        @parameter
        fn body_func[i: Int]() -> None:
            coefficients[i] = self.get[i]()

        fori_loop[body_func, 0, num_terms, 1]()

        return Chebyshev[num_terms, dtype, simd_width] {
            _coefficients: coefficients
        }

    fn economize[error_tolerance: Scalar[dtype]](self) -> Int:
        """Economizes the Chebyshev series by minimizing the number of terms.

        Given a Chebyshev series `p` with `n` terms, this function returns the minimum
        number of terms `m` such that the condition

        `|p(x) - p_m(x)| <= |c[m]| + ... + |c[n-1]| <= error_tolerance`

        holds for all `x` in the interval [-1, 1], where `p_m` is the Chebyshev series
        obtained by truncating `p` to `m <= n` terms.

        Parameters:
            error_tolerance: The tolerance for the approximation error between the
                original series and its truncated version with `m` terms.

        Returns:
            The minimum number of terms `m` required to ensure the approximation error
            between the original series and its truncated version with `m` terms is less
            than or equal to `error_tolerance`.

        Constraints:
            The error tolerance must be positive.
        """
        asserting.assert_positive[dtype, "error_tolerance", error_tolerance]()

        var num_terms_required = num_terms
        var error = Scalar[dtype](0.0)

        @always_inline
        @parameter
        fn body_func[i: Int]() -> Bool:
            # For any coefficient `c` of the Chebyshev series `p`, the following condition
            # holds: `c[0] == c[1] == ... == c[simd_width - 1]`.
            var value = self.get[i]()[0]
            # TODO: Use `abs` when the problem evaluating it in compile-time is fixed.
            # https://github.com/modularml/mojo/issues/1244
            if value < 0:
                error -= value
            else:
                error += value

            if error > error_tolerance:
                num_terms_required = i + 1
                return False

            return True

        fori_loop[body_func, num_terms - 1, 0, -1]()

        return num_terms_required

    fn __call__(self, x: SIMD[dtype, simd_width]) -> SIMD[dtype, simd_width]:
        """Evaluates the Chebyshev series at `x` using the Clenshaw algorithm.

        If the series `p` has `n + 1` terms, this function computes `p(x)` element-wise:

        `p(x) = c[0] * T[0](x) + c[1] * T[1](x) + ... + c[n] * T[n](x)`.

        Args:
            x: A SIMD vector containing the points at which to evaluate the Chebyshev
                series. These points should be within the interval `[-1, 1]`.

        Returns:
            A SIMD vector containing the values of the Chebyshev series evaluated at
            the points specified by `x`.
        """
        alias nan: SIMD[dtype, simd_width] = math.nan[dtype]()

        var result = SIMD[dtype, simd_width](0.0)

        @parameter
        if num_terms == 1:
            result = self.get[0]()
        elif num_terms == 2:
            result = math.fma(self.get[1](), x, self.get[0]())
        else:
            var two_x = 2.0 * x
            var tmp = SIMD[dtype, simd_width](0.0)
            var c0 = self.get[num_terms - 2]()
            var c1 = self.get[num_terms - 1]()

            @always_inline
            @parameter
            fn body_func[i: Int]() -> None:
                tmp = c0
                c0 = self.get[i]() - c1
                c1 = math.fma(c1, two_x, tmp)

            fori_loop[body_func, num_terms - 3, -1, -1]()

            result = math.fma(c1, x, c0)

        return (abs(x) > 1.0).select(nan, result)


# ===---------------------------- Power Series ----------------------------=== #


@register_passable("trivial")
struct Polynomial[
    num_terms: Int,
    dtype: DType,
    simd_width: Int,
](Sized):
    """Represents a finite Power series, commonly known as a polynomial.

    A Power series with `n + 1` terms is a linear combination of the form

    `p(x) = c[0] + c[1] * x + ... + c[n] * x**n`

    where `x` is the independent variable and `c[i]` is the `i`-th coefficient of the
    series.

    Parameters:
        num_terms: The number of terms in the series.
        dtype: The data type of the independent variable `x` in the series.
        simd_width: The SIMD width of the independent variable `x` in the series.

    Constraints:
        The number of terms must be positive. The parameter `dtype` must be `float32`
        or `float64`.
    """

    var _coefficients: StaticTuple[SIMD[dtype, simd_width], num_terms]

    @staticmethod
    fn from_coefficients[*coefficients: Scalar[dtype]]() -> Self:
        """Generates a Power series from a sequence of coefficients.

        Parameters:
            coefficients: The sequence of coefficients in order of increasing degree,
                i.e., `(1, 2, 3)` gives `1 + 2 * x + 3 * x**2`.

        Returns:
            A Power series with the given coefficients.

        Constraints:
            The number of coefficients must be equal to the parameter `num_terms`.
        """
        _check_polynomial_like_constraints[num_terms, dtype, simd_width]()

        alias coefficients_list = VariadicList(coefficients)

        constrained[
            num_terms == len(coefficients_list),
            (
                "The number of coefficients must be equal to the parameter"
                " `num_terms`."
            ),
        ]()

        var splatted_coefficients = StaticTuple[
            SIMD[dtype, simd_width], num_terms
        ]()

        for i in range(num_terms):
            splatted_coefficients[i] = coefficients_list[i]

        return Self {_coefficients: splatted_coefficients}

    @staticmethod
    fn from_hexadecimal_coefficients[
        *coefficients: Scalar[get_hexadecimal_dtype[dtype]()]
    ]() -> Self:
        """Generates a Power series from a sequence of hexadecimal coefficients.

        The data type used for hexadecimal representation of the coefficients is
        automatically determined based on `dtype`: `uint32` if `dtype` is `float32`
        or `uint64` if `dtype` is `float64`.

        Parameters:
            coefficients: The sequence of hexadecimal coefficients in order of
                increasing degree. See the method `from_coefficients`.

        Returns:
            A Power series with the given hexadecimal coefficients.

        Constraints:
            The number of hexadecimal coefficients must be equal to the parameter
            `num_terms`.
        """
        _check_polynomial_like_constraints[num_terms, dtype, simd_width]()

        alias coefficients_list = VariadicList(coefficients)

        constrained[
            num_terms == len(coefficients_list),
            (
                "The number of hexadecimal coefficients must be equal to the"
                " parameter `num_terms`."
            ),
        ]()

        var splatted_coefficients = StaticTuple[
            SIMD[dtype, simd_width], num_terms
        ]()

        for i in range(num_terms):
            splatted_coefficients[i] = bitcast[dtype](coefficients_list[i])

        return Self {_coefficients: splatted_coefficients}

    @always_inline
    fn __len__(self: Self) -> Int:
        """Returns the number of terms in the Power series.

        This is known at compile time.

        Returns:
            The number of terms in the Power series.
        """
        return num_terms

    @always_inline
    fn degree(self: Self) -> Int:
        """Returns the degree of the Power series."""
        return num_terms - 1

    @always_inline
    fn get[index: Int](self: Self) -> SIMD[dtype, simd_width]:
        """Returns the coefficient of the Power series at the given index.

        Parameters:
            index: The index of the coefficient to return.

        Returns:
            A SIMD vector containing the coefficient of the Power series at
            the given index.

        Constraints:
            The index must be within the range `[0, num_terms)`.
        """
        asserting.assert_in_range["index", index, 0, num_terms]()
        return self._coefficients[index]

    fn truncate[
        num_terms: Int
    ](self: Self) -> Polynomial[num_terms, dtype, simd_width]:
        """Truncates the Power series by discarding high-degree terms.

        Parameters:
            num_terms: The number of terms in the truncated series.

        Returns:
            A truncated Power series.

        Constraints:
            The number of terms in the truncated series must be positive and less than
            or equal to the number of terms in the original series.
        """
        asserting.assert_in_range[
            "num_terms", num_terms, 1, self.num_terms + 1
        ]()
        var coefficients = StaticTuple[SIMD[dtype, simd_width], num_terms]()

        @always_inline
        @parameter
        fn body_func[i: Int]() -> None:
            coefficients[i] = self.get[i]()

        fori_loop[body_func, 0, num_terms, 1]()

        return Polynomial[num_terms, dtype, simd_width] {
            _coefficients: coefficients
        }

    fn __call__(self, x: SIMD[dtype, simd_width]) -> SIMD[dtype, simd_width]:
        """Evaluates the Power series at `x` using the Horner's scheme.

        If the series `p` has `n + 1` terms, this function computes `p(x)` element-wise:

        `p(x) = c[0] + c[1] * x + ... + c[n] * x**n`.

        Args:
            x: A SIMD vector containing the points at which to evaluate the Power
                series.

        Returns:
            A SIMD vector containing the values of the Power series evaluated at the
            points specified by `x`.
        """
        # In terms of accuracy or execution time, there is no reason to use
        # `math.polynomial_evaluate`. See the Jupyter notebooks below:
        # - https://github.com/leandrolcampos/specials/blob/polynomial_evaluate/polyval_f32.ipynb
        # - https://github.com/leandrolcampos/specials/blob/polynomial_evaluate/polyval_f64.ipynb

        var result = self.get[num_terms - 1]()

        @parameter
        if num_terms > 1:

            @always_inline
            @parameter
            fn body_func[i: Int]() -> None:
                result = math.fma(result, x, self.get[i]())

            fori_loop[body_func, num_terms - 2, -1, -1]()

        return result


--- src/specials/_internal/table.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2023 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #

"""Implements utilities for table-based numerical methods."""

import math

from memory.unsafe import bitcast
from utils.static_tuple import StaticTuple

from specials._internal import asserting


@always_inline
fn get_hexadecimal_dtype[decimal_dtype: DType]() -> DType:
    """Returns the hexadecimal dtype corresponding to the provided decimal dtype.
    """

    @parameter
    if decimal_dtype == DType.float32:
        return DType.uint32
    elif decimal_dtype == DType.float64:
        return DType.uint64
    else:
        return DType.invalid


@always_inline
fn _check_float_table_constraints[size: Int, dtype: DType]() -> None:
    """Checks the constraints of the `FloatTable`."""
    asserting.assert_positive["size", size]()
    asserting.assert_float_dtype["dtype", dtype]()


@register_passable("trivial")
struct FloatTable[size: Int, dtype: DType](Sized):
    """Represents a table of floating-point values.

    It is used to implement table lookup algorithms.

    Parameters:
        size: The number of floating-point values in the table.
        dtype: The data type of the floating-point values.

    Constraints:
        The size must be positive. The parameter `dtype` must be `float32` or `float64`.
    """

    var _data: StaticTuple[Scalar[dtype], size]

    @staticmethod
    fn from_values[*values: Scalar[dtype]]() -> Self:
        """Creates a table from a sequence of floating-point values.

        Parameters:
            values: The sequence of floating-point values.

        Returns:
            A `FloatTable` with the given floating-point values.

        Constraints:
            The number of values must be equal to the parameter `size`.
        """
        _check_float_table_constraints[size, dtype]()

        constrained[
            size == len(VariadicList(values)),
            "The number of values must be equal to the parameter `size`.",
        ]()

        return Self {_data: StaticTuple[Scalar[dtype], size](values)}

    @staticmethod
    fn from_hexadecimal_values[
        *values: Scalar[get_hexadecimal_dtype[dtype]()]
    ]() -> Self:
        """Creates a table from a sequence of hexadecimal floating-point values.

        The data type used for hexadecimal representation of the floating-point values
        is automatically determined based on `dtype`: `uint32` if `dtype` is `float32`
        or `uint64` if `dtype` is `float64`.

        Parameters:
            values: The sequence of hexadecimal floating-point values.

        Returns:
            A `FloatTable` with the given hexadecimal floating-point values.

        Constraints:
            The number of hexadecimal values must be equal to the parameter `size`.
        """
        _check_float_table_constraints[size, dtype]()

        alias values_list = VariadicList(values)

        constrained[
            size == len(values_list),
            (
                "The number of hexadecimal values must be equal to the"
                " parameter `size`."
            ),
        ]()

        var data = StaticTuple[Scalar[dtype], size]()

        for i in range(size):
            data[i] = bitcast[dtype](values_list[i])

        return Self {_data: data}

    @always_inline
    fn __len__(self: Self) -> Int:
        """Returns the number of floating-point values in the table.

        This is known at compile time.

        Returns:
            The number of floating-point values in the table.
        """
        return size

    @always_inline
    fn get[index: Int](self: Self) -> Scalar[dtype]:
        """Returns the floating-point value of the table at the given index.

        Parameters:
            index: The index of the floating-point value to return.

        Returns:
            SIMD vector containing the floating-point value of the table at the given
            index.

        Constraints:
            The index must be in the range `[0, size)`.
        """
        asserting.assert_in_range["index", index, 0, size]()
        return self._data[index]

    @always_inline
    fn unsafe_lookup(self: Self, index: SIMD) -> SIMD[dtype, index.size]:
        """Returns the floating-point values of the table at the given indices.

        For performance reasons, this method does not perform bounds checking.

        Args:
            index: SIMD vector containing the indices of the floating-point values
                to return.

        Returns:
            SIMD vector containing the floating-point values of the table at the
            given indices.

        Constraints:
            The parameter `index.type` must be an integer.
        """
        # TODO: Use the overload of `DTypePointer.simd_load` when it is available.
        # This overload will allow to load a SIMD vector of floating-point values
        # from a SIMD vector of indices. It may require to change the underlying
        # data storage to a `DTypePointer`. See the feature request:
        # https://github.com/modularml/mojo/issues/1626

        asserting.assert_integral_dtype["index.type", index.type]()
        var result = SIMD[dtype, index.size]()

        @parameter
        for i in range(index.size):
            result[i] = self._data[int(index[i])]

        return result

    fn lookup(self: Self, index: SIMD) -> SIMD[dtype, index.size]:
        """Returns the floating-point values of the table at the given indices.

        Args:
            index: SIMD vector containing the indices of the floating-point values
                to return.

        Returns:
            SIMD vector containing the floating-point values of the table at the
            given indices. If an index is out of range, the corresponding result
            is `NaN`.

        Constraints:
            The parameter `index.type` must be an integer.
        """
        asserting.assert_integral_dtype["index.type", index.type]()

        var is_safe = (index >= 0) & (index < size)
        var safe_index = is_safe.select(index, 0)

        return is_safe.select(self.unsafe_lookup(safe_index), math.nan[dtype]())


--- src/specials/elementary/__init__.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2023 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #

"""Provides elementary functions."""


--- src/specials/elementary/common_constants.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #

"""Defines common constants for elementary function implementations."""

from specials._internal.asserting import assert_float_dtype
from specials._internal.table import FloatTable


# ===----------------------- Exponential Functions ------------------------=== #
#
# References:
#
# Tang, P. T. P. (1989). Table-driven implementation of the exponential function
#   in IEEE floating-point arithmetic.
# ACM Transactions on Mathematical Software (TOMS), 15(2), 144-157.
# https://doi.org/10.1145/63522.214389


@always_inline
fn _get_exp_lead_table[dtype: DType]() -> FloatTable[32, dtype]:
    """Returns the table entries of `exp_lead` for single or double precision.
    """

    @parameter
    if dtype == DType.float32:
        return FloatTable[32, dtype].from_hexadecimal_values[
            0x3F80_0000,
            0x3F82_CD80,
            0x3F85_AAC0,
            0x3F88_9800,
            0x3F8B_95C0,
            0x3F8E_A400,
            0x3F91_C3C0,
            0x3F94_F4C0,
            0x3F98_37C0,
            0x3F9B_8D00,
            0x3F9E_F500,
            0x3FA2_7040,
            0x3FA5_FEC0,
            0x3FA9_A140,
            0x3FAD_5800,
            0x3FB1_23C0,
            0x3FB5_04C0,
            0x3FB8_FB80,
            0x3FBD_0880,
            0x3FC1_2C40,
            0x3FC5_6700,
            0x3FC9_B980,
            0x3FCE_2480,
            0x3FD2_A800,
            0x3FD7_44C0,
            0x3FDB_FB80,
            0x3FE0_CCC0,
            0x3FE5_B900,
            0x3FEA_C0C0,
            0x3FEF_E480,
            0x3FF5_2540,
            0x3FFA_8380,
        ]()
    else:  # dtype == DType.float64
        return FloatTable[32, dtype].from_hexadecimal_values[
            0x3FF00000_00000000,
            0x3FF059B0_D3158540,
            0x3FF0B558_6CF98900,
            0x3FF11301_D0125B40,
            0x3FF172B8_3C7D5140,
            0x3FF1D487_3168B980,
            0x3FF2387A_6E756200,
            0x3FF29E9D_F51FDEC0,
            0x3FF306FE_0A31B700,
            0x3FF371A7_373AA9C0,
            0x3FF3DEA6_4C123400,
            0x3FF44E08_60618900,
            0x3FF4BFDA_D5362A00,
            0x3FF5342B_569D4F80,
            0x3FF5AB07_DD485400,
            0x3FF6247E_B03A5580,
            0x3FF6A09E_667F3BC0,
            0x3FF71F75_E8EC5F40,
            0x3FF7A114_73EB0180,
            0x3FF82589_994CCE00,
            0x3FF8ACE5_422AA0C0,
            0x3FF93737_B0CDC5C0,
            0x3FF9C491_82A3F080,
            0x3FFA5503_B23E2540,
            0x3FFAE89F_995AD380,
            0x3FFB7F76_F2FB5E40,
            0x3FFC199B_DD855280,
            0x3FFCB720_DCEF9040,
            0x3FFD5818_DCFBA480,
            0x3FFDFC97_337B9B40,
            0x3FFEA4AF_A2A490C0,
            0x3FFF5076_5B6E4540,
        ]()


@always_inline
fn _get_exp_trail_table[dtype: DType]() -> FloatTable[32, dtype]:
    """Returns the table entries of `exp_trail` for single or double precision.
    """

    @parameter
    if dtype == DType.float32:
        return FloatTable[32, dtype].from_hexadecimal_values[
            0x0000_0000,
            0x3553_1585,
            0x34D9_F312,
            0x35E8_092E,
            0x3471_F546,
            0x36E6_2D17,
            0x361B_9D59,
            0x36BE_A3FC,
            0x36C1_4637,
            0x36E6_E755,
            0x36C9_8247,
            0x34C0_C312,
            0x3635_4D8B,
            0x3655_A754,
            0x36FB_A90B,
            0x36D6_074B,
            0x36CC_CFE7,
            0x36BD_1D8C,
            0x368E_7D60,
            0x35CC_A667,
            0x36A8_4554,
            0x36F6_19B9,
            0x35C1_51F8,
            0x366C_8F89,
            0x36F3_2B5A,
            0x36DE_5F6C,
            0x3677_6155,
            0x355C_EF90,
            0x355C_FBA5,
            0x36E6_6F73,
            0x36F4_5492,
            0x36CB_6DC9,
        ]()
    else:  # dtype == DType.float64
        return FloatTable[32, dtype].from_hexadecimal_values[
            0x00000000_00000000,
            0x3D0A1D73_E2A475B4,
            0x3CEEC531_7256E308,
            0x3CF0A4EB_BF1AED93,
            0x3D0D6E6F_BE462876,
            0x3D053C02_DC0144C8,
            0x3D0C3360_FD6D8E0B,
            0x3D009612_E8AFAD12,
            0x3CF52DE8_D5A46306,
            0x3CE54E28_AA05E8A9,
            0x3D011ADA_0911F09F,
            0x3D068189_B7A04EF8,
            0x3D038EA1_CBD7F621,
            0x3CBDF0A8_3C49D86A,
            0x3D04AC64_980A8C8F,
            0x3CD2C7C3_E81BF4B7,
            0x3CE92116_5F626CDD,
            0x3D09EE91_B8797785,
            0x3CDB5F54_408FDB37,
            0x3CF28ACF_88AFAB35,
            0x3CFB5BA7_C55A192D,
            0x3D027A28_0E1F92A0,
            0x3CF01C7C_46B071F3,
            0x3CFC8B42_4491CAF8,
            0x3D06AF43_9A68BB99,
            0x3CDBAA9E_C206AD4F,
            0x3CFC2220_CB12A092,
            0x3D048A81_E5E8F4A5,
            0x3CDC9768_16BAD9B8,
            0x3CFEB968_CAC39ED3,
            0x3CF9858F_73A18F5E,
            0x3C99D3E1_2DD8A18B,
        ]()


@register_passable("trivial")
struct ExpTable[dtype: DType]:
    """Table entries of `exp_lead` and `exp_trail` for single or double precision.
    """

    alias lead = _get_exp_lead_table[dtype]()
    alias trail = _get_exp_trail_table[dtype]()


# ===----------------------------------------------------------------------=== #
# Logarithm Functions
# ===----------------------------------------------------------------------=== #


@always_inline("nodebug")
fn _get_inv_fraction1[dtype: DType]() -> FloatTable[129, dtype]:
    """Returns the table entries of `inv_fraction1`."""
    assert_float_dtype["dtype", dtype]()

    @parameter
    if dtype == DType.float32:
        return FloatTable[129, dtype].from_hexadecimal_values[
            0x3F80_0000,
            0x3F7E_03F8,
            0x3F7C_0FC1,
            0x3F7A_232D,
            0x3F78_3E10,
            0x3F76_603E,
            0x3F74_898D,
            0x3F72_B9D6,
            0x3F70_F0F1,
            0x3F6F_2EB7,
            0x3F6D_7304,
            0x3F6B_BDB3,
            0x3F6A_0EA1,
            0x3F68_65AC,
            0x3F66_C2B4,
            0x3F65_2598,
            0x3F63_8E39,
            0x3F61_FC78,
            0x3F60_7038,
            0x3F5E_E95C,
            0x3F5D_67C9,
            0x3F5B_EB62,
            0x3F5A_740E,
            0x3F59_01B2,
            0x3F57_9436,
            0x3F56_2B81,
            0x3F54_C77B,
            0x3F53_680D,
            0x3F52_0D21,
            0x3F50_B6A0,
            0x3F4F_6475,
            0x3F4E_168A,
            0x3F4C_CCCD,
            0x3F4B_8728,
            0x3F4A_4588,
            0x3F49_07DA,
            0x3F47_CE0C,
            0x3F46_980C,
            0x3F45_65C8,
            0x3F44_3730,
            0x3F43_0C31,
            0x3F41_E4BC,
            0x3F40_C0C1,
            0x3F3F_A030,
            0x3F3E_82FA,
            0x3F3D_6910,
            0x3F3C_5264,
            0x3F3B_3EE7,
            0x3F3A_2E8C,
            0x3F39_2144,
            0x3F38_1703,
            0x3F37_0FBB,
            0x3F36_0B61,
            0x3F35_09E7,
            0x3F34_0B41,
            0x3F33_0F63,
            0x3F32_1643,
            0x3F31_1FD4,
            0x3F30_2C0B,
            0x3F2F_3ADE,
            0x3F2E_4C41,
            0x3F2D_602B,
            0x3F2C_7692,
            0x3F2B_8F6A,
            0x3F2A_AAAB,
            0x3F29_C84A,
            0x3F28_E83F,
            0x3F28_0A81,
            0x3F27_2F05,
            0x3F26_55C4,
            0x3F25_7EB5,
            0x3F24_A9CF,
            0x3F23_D70A,
            0x3F23_065E,
            0x3F22_37C3,
            0x3F21_6B31,
            0x3F20_A0A1,
            0x3F1F_D80A,
            0x3F1F_1166,
            0x3F1E_4CAD,
            0x3F1D_89D9,
            0x3F1C_C8E1,
            0x3F1C_09C1,
            0x3F1B_4C70,
            0x3F1A_90E8,
            0x3F19_D723,
            0x3F19_1F1A,
            0x3F18_68C8,
            0x3F17_B426,
            0x3F17_012E,
            0x3F16_4FDA,
            0x3F15_A025,
            0x3F14_F209,
            0x3F14_4581,
            0x3F13_9A86,
            0x3F12_F114,
            0x3F12_4925,
            0x3F11_A2B4,
            0x3F10_FDBC,
            0x3F10_5A38,
            0x3F0F_B824,
            0x3F0F_177A,
            0x3F0E_7835,
            0x3F0D_DA52,
            0x3F0D_3DCB,
            0x3F0C_A29C,
            0x3F0C_08C1,
            0x3F0B_7034,
            0x3F0A_D8F3,
            0x3F0A_42F8,
            0x3F09_AE41,
            0x3F09_1AC7,
            0x3F08_8889,
            0x3F07_F781,
            0x3F07_67AB,
            0x3F06_D905,
            0x3F06_4B8A,
            0x3F05_BF37,
            0x3F05_3408,
            0x3F04_A9FA,
            0x3F04_2108,
            0x3F03_9930,
            0x3F03_126F,
            0x3F02_8CC0,
            0x3F02_0821,
            0x3F01_848E,
            0x3F01_0204,
            0x3F00_8081,
            0x3F00_0000,
        ]()
    else:  # dtype == DType.float64
        return FloatTable[129, dtype].from_hexadecimal_values[
            0x3FF00000_00000000,
            0x3FEFC07F_01FC07F0,
            0x3FEF81F8_1F81F820,
            0x3FEF4465_9E4A4271,
            0x3FEF07C1_F07C1F08,
            0x3FEECC07_B301ECC0,
            0x3FEE9131_ABF0B767,
            0x3FEE573A_C901E574,
            0x3FEE1E1E_1E1E1E1E,
            0x3FEDE5D6_E3F8868A,
            0x3FEDAE60_76B981DB,
            0x3FED77B6_54B82C34,
            0x3FED41D4_1D41D41D,
            0x3FED0CB5_8F6EC074,
            0x3FECD856_89039B0B,
            0x3FECA4B3_055EE191,
            0x3FEC71C7_1C71C71C,
            0x3FEC3F8F_01C3F8F0,
            0x3FEC0E07_0381C0E0,
            0x3FEBDD2B_899406F7,
            0x3FEBACF9_14C1BAD0,
            0x3FEB7D6C_3DDA338B,
            0x3FEB4E81_B4E81B4F,
            0x3FEB2036_406C80D9,
            0x3FEAF286_BCA1AF28,
            0x3FEAC570_1AC5701B,
            0x3FEA98EF_606A63BE,
            0x3FEA6D01_A6D01A6D,
            0x3FEA41A4_1A41A41A,
            0x3FEA16D3_F97A4B02,
            0x3FE9EC8E_951033D9,
            0x3FE9C2D1_4EE4A102,
            0x3FE99999_9999999A,
            0x3FE970E4_F80CB872,
            0x3FE948B0_FCD6E9E0,
            0x3FE920FB_49D0E229,
            0x3FE8F9C1_8F9C18FA,
            0x3FE8D301_8D3018D3,
            0x3FE8ACB9_0F6BF3AA,
            0x3FE886E5_F0ABB04A,
            0x3FE86186_18618618,
            0x3FE83C97_7AB2BEDD,
            0x3FE81818_18181818,
            0x3FE7F405_FD017F40,
            0x3FE7D05F_417D05F4,
            0x3FE7AD22_08E0ECC3,
            0x3FE78A4C_8178A4C8,
            0x3FE767DC_E434A9B1,
            0x3FE745D1_745D1746,
            0x3FE72428_7F46DEBC,
            0x3FE702E0_5C0B8170,
            0x3FE6E1F7_6B4337C7,
            0x3FE6C16C_16C16C17,
            0x3FE6A13C_D1537290,
            0x3FE68168_16816817,
            0x3FE661EC_6A5122F9,
            0x3FE642C8_590B2164,
            0x3FE623FA_77016240,
            0x3FE60581_60581606,
            0x3FE5E75B_B8D015E7,
            0x3FE5C988_2B931057,
            0x3FE5AC05_6B015AC0,
            0x3FE58ED2_308158ED,
            0x3FE571ED_3C506B3A,
            0x3FE55555_55555555,
            0x3FE53909_48F40FEB,
            0x3FE51D07_EAE2F815,
            0x3FE50150_15015015,
            0x3FE4E5E0_A72F0539,
            0x3FE4CAB8_8725AF6E,
            0x3FE4AFD6_A052BF5B,
            0x3FE49539_E3B2D067,
            0x3FE47AE1_47AE147B,
            0x3FE460CB_C7F5CF9A,
            0x3FE446F8_6562D9FB,
            0x3FE42D66_25D51F87,
            0x3FE41414_14141414,
            0x3FE3FB01_3FB013FB,
            0x3FE3E22C_BCE4A902,
            0x3FE3C995_A47BABE7,
            0x3FE3B13B_13B13B14,
            0x3FE3991C_2C187F63,
            0x3FE38138_13813814,
            0x3FE3698D_F3DE0748,
            0x3FE3521C_FB2B78C1,
            0x3FE33AE4_5B57BCB2,
            0x3FE323E3_4A2B10BF,
            0x3FE30D19_0130D190,
            0x3FE2F684_BDA12F68,
            0x3FE2E025_C04B8097,
            0x3FE2C9FB_4D812CA0,
            0x3FE2B404_AD012B40,
            0x3FE29E41_29E4129E,
            0x3FE288B0_1288B013,
            0x3FE27350_B8812735,
            0x3FE25E22_708092F1,
            0x3FE24924_92492492,
            0x3FE23456_789ABCDF,
            0x3FE21FB7_8121FB78,
            0x3FE20B47_0C67C0D9,
            0x3FE1F704_7DC11F70,
            0x3FE1E2EF_3B3FB874,
            0x3FE1CF06_ADA2811D,
            0x3FE1BB4A_4046ED29,
            0x3FE1A7B9_611A7B96,
            0x3FE19453_808CA29C,
            0x3FE18118_11811812,
            0x3FE16E06_89427379,
            0x3FE15B1E_5F75270D,
            0x3FE1485F_0E0ACD3B,
            0x3FE135C8_1135C811,
            0x3FE12358_E75D3033,
            0x3FE11111_11111111,
            0x3FE0FEF0_10FEF011,
            0x3FE0ECF5_6BE69C90,
            0x3FE0DB20_A88F4696,
            0x3FE0C971_4FBCDA3B,
            0x3FE0B7E6_EC259DC8,
            0x3FE0A681_0A6810A7,
            0x3FE0953F_39010954,
            0x3FE08421_08421084,
            0x3FE07326_0A47F7C6,
            0x3FE0624D_D2F1A9FC,
            0x3FE05197_F7D73404,
            0x3FE04104_10410410,
            0x3FE03091_B51F5E1A,
            0x3FE02040_81020408,
            0x3FE01010_10101010,
            0x3FE00000_00000000,
        ]()


@always_inline("nodebug")
fn _get_log_fraction1_lead[dtype: DType]() -> FloatTable[129, dtype]:
    """Returns the table entries of `log_fraction1_lead`."""
    assert_float_dtype["dtype", dtype]()

    @parameter
    if dtype == DType.float32:
        return FloatTable[129, dtype].from_hexadecimal_values[
            0x0000_0000,
            0x3BFF_0000,
            0x3C7E_0000,
            0x3CBD_C000,
            0x3CFC_0000,
            0x3D1C_F000,
            0x3D3B_A000,
            0x3D5A_1000,
            0x3D78_5000,
            0x3D8B_2800,
            0x3D9A_0800,
            0x3DA8_D800,
            0x3DB7_8000,
            0x3DC6_1800,
            0x3DD4_9000,
            0x3DE2_F000,
            0x3DF1_3800,
            0x3DFF_6000,
            0x3E06_B800,
            0x3E0D_B800,
            0x3E14_A800,
            0x3E1B_8C00,
            0x3E22_6800,
            0x3E29_3400,
            0x3E2F_F800,
            0x3E36_B000,
            0x3E3D_5C00,
            0x3E43_FC00,
            0x3E4A_9000,
            0x3E51_1C00,
            0x3E57_9C00,
            0x3E5E_1400,
            0x3E64_7C00,
            0x3E6A_E000,
            0x3E71_3800,
            0x3E77_8400,
            0x3E7D_C800,
            0x3E82_0000,
            0x3E85_1800,
            0x3E88_2C00,
            0x3E8B_3A00,
            0x3E8E_4400,
            0x3E91_4A00,
            0x3E94_4A00,
            0x3E97_4600,
            0x3E9A_3E00,
            0x3E9D_3200,
            0x3EA0_2000,
            0x3EA3_0C00,
            0x3EA5_F200,
            0x3EA8_D400,
            0x3EAB_B200,
            0x3EAE_8C00,
            0x3EB1_6400,
            0x3EB4_3600,
            0x3EB7_0400,
            0x3EB9_CE00,
            0x3EBC_9400,
            0x3EBF_5600,
            0x3EC2_1600,
            0x3EC4_D000,
            0x3EC7_8800,
            0x3ECA_3C00,
            0x3ECC_EC00,
            0x3ECF_9800,
            0x3ED2_4200,
            0x3ED4_E600,
            0x3ED7_8800,
            0x3EDA_2600,
            0x3EDC_C200,
            0x3EDF_5A00,
            0x3EE1_EE00,
            0x3EE4_7E00,
            0x3EE7_0C00,
            0x3EE9_9600,
            0x3EEC_1E00,
            0x3EEE_A200,
            0x3EF1_2400,
            0x3EF3_A200,
            0x3EF6_1C00,
            0x3EF8_9400,
            0x3EFB_0800,
            0x3EFD_7A00,
            0x3EFF_E800,
            0x3F01_2A00,
            0x3F02_5E00,
            0x3F03_9100,
            0x3F04_C300,
            0x3F05_F300,
            0x3F07_2200,
            0x3F08_4F00,
            0x3F09_7B00,
            0x3F0A_A600,
            0x3F0B_CF00,
            0x3F0C_F700,
            0x3F0E_1D00,
            0x3F0F_4200,
            0x3F10_6600,
            0x3F11_8900,
            0x3F12_AA00,
            0x3F13_CA00,
            0x3F14_E900,
            0x3F16_0700,
            0x3F17_2300,
            0x3F18_3E00,
            0x3F19_5800,
            0x3F1A_7100,
            0x3F1B_8800,
            0x3F1C_9F00,
            0x3F1D_B400,
            0x3F1E_C800,
            0x3F1F_DA00,
            0x3F20_EC00,
            0x3F21_FC00,
            0x3F23_0C00,
            0x3F24_1A00,
            0x3F25_2700,
            0x3F26_3300,
            0x3F27_3E00,
            0x3F28_4800,
            0x3F29_5100,
            0x3F2A_5900,
            0x3F2B_5F00,
            0x3F2C_6500,
            0x3F2D_6A00,
            0x3F2E_6D00,
            0x3F2F_7000,
            0x3F30_7100,
            0x3F31_7200,
        ]()
    else:  # dtype == DType.float64
        return FloatTable[129, dtype].from_hexadecimal_values[
            0x00000000_00000000,
            0x3F7FE02A_6B000000,
            0x3F8FC0A8_B0F00000,
            0x3F97B91B_07D00000,
            0x3F9F829B_0E780000,
            0x3FA39E87_B9FC0000,
            0x3FA77458_F6300000,
            0x3FAB42DD_71180000,
            0x3FAF0A30_C0100000,
            0x3FB16536_EEA20000,
            0x3FB341D7_961A0000,
            0x3FB51B07_3F060000,
            0x3FB6F0D2_8AE40000,
            0x3FB8C345_D6300000,
            0x3FBA926D_3A4A0000,
            0x3FBC5E54_8F5A0000,
            0x3FBE2707_6E2A0000,
            0x3FBFEC91_31DA0000,
            0x3FC0D77E_7CD00000,
            0x3FC1B72A_D52F0000,
            0x3FC29552_F81F0000,
            0x3FC371FC_201E0000,
            0x3FC44D2B_6CCB0000,
            0x3FC526E5_E3A10000,
            0x3FC5FF30_70A70000,
            0x3FC6D60F_E7190000,
            0x3FC7AB89_02100000,
            0x3FC87FA0_65200000,
            0x3FC9525A_9CF40000,
            0x3FCA23BC_1FE20000,
            0x3FCAF3C9_4E800000,
            0x3FCBC286_742D0000,
            0x3FCC8FF7_C79A0000,
            0x3FCD5C21_6B4F0000,
            0x3FCE2707_6E2A0000,
            0x3FCEF0AD_CBDC0000,
            0x3FCFB918_6D5E0000,
            0x3FD04025_94B48000,
            0x3FD0A324_E2738000,
            0x3FD1058B_F9AE0000,
            0x3FD1675C_ABAB8000,
            0x3FD1C898_C1698000,
            0x3FD22941_FBCF0000,
            0x3FD2895A_13DE8000,
            0x3FD2E8E2_BAE10000,
            0x3FD347DD_9A980000,
            0x3FD3A64C_55690000,
            0x3FD40430_86868000,
            0x3FD4618B_C21C0000,
            0x3FD4BE5F_95770000,
            0x3FD51AAD_872D8000,
            0x3FD57677_17450000,
            0x3FD5D1BD_BF580000,
            0x3FD62C82_F2B98000,
            0x3FD686C8_1E9B0000,
            0x3FD6E08E_AA2B8000,
            0x3FD739D7_F6BB8000,
            0x3FD792A5_5FDD0000,
            0x3FD7EAF8_3B828000,
            0x3FD842D1_DA1E8000,
            0x3FD89A33_86C10000,
            0x3FD8F11E_87360000,
            0x3FD94794_1C210000,
            0x3FD99D95_81178000,
            0x3FD9F323_ECBF8000,
            0x3FDA4840_90E58000,
            0x3FDA9CEC_9A9A0000,
            0x3FDAF129_32470000,
            0x3FDB44F7_7BCC8000,
            0x3FDB9858_96930000,
            0x3FDBEB4D_9DA70000,
            0x3FDC3DD7_A7CD8000,
            0x3FDC8FF7_C79A8000,
            0x3FDCE1AF_0B858000,
            0x3FDD32FE_7E008000,
            0x3FDD83E7_258A0000,
            0x3FDDD46A_04C18000,
            0x3FDE2488_1A7C0000,
            0x3FDE7442_61D68000,
            0x3FDEC399_D2468000,
            0x3FDF128F_5FAF0000,
            0x3FDF6123_FA700000,
            0x3FDFAF58_8F788000,
            0x3FDFFD2E_08578000,
            0x3FE02552_A5A5C000,
            0x3FE04BDF_9DA90000,
            0x3FE0723E_5C1CC000,
            0x3FE0986F_4F570000,
            0x3FE0BE72_E4250000,
            0x3FE0E449_85D1C000,
            0x3FE109F3_9E2D4000,
            0x3FE12F71_9593C000,
            0x3FE154C3_D2F4C000,
            0x3FE179EA_BBD88000,
            0x3FE19EE6_B467C000,
            0x3FE1C3B8_1F710000,
            0x3FE1E85F_5E704000,
            0x3FE20CDC_D1928000,
            0x3FE23130_D7BE8000,
            0x3FE2555B_CE98C000,
            0x3FE2795E_12898000,
            0x3FE29D37_FEC28000,
            0x3FE2C0E9_ED448000,
            0x3FE2E474_36E40000,
            0x3FE307D7_334F0000,
            0x3FE32B13_39120000,
            0x3FE34E28_9D9CC000,
            0x3FE37117_B5474000,
            0x3FE393E0_D3560000,
            0x3FE3B684_49FFC000,
            0x3FE3D902_6A714000,
            0x3FE3FB5B_84D14000,
            0x3FE41D8F_E8464000,
            0x3FE43F9F_E2F9C000,
            0x3FE4618B_C21C4000,
            0x3FE48353_D1EA8000,
            0x3FE4A4F8_5DB00000,
            0x3FE4C679_AFCCC000,
            0x3FE4E7D8_11B74000,
            0x3FE50913_CC014000,
            0x3FE52A2D_265BC000,
            0x3FE54B24_67998000,
            0x3FE56BF9_D5B3C000,
            0x3FE58CAD_B5CD4000,
            0x3FE5AD40_4C358000,
            0x3FE5CDB1_DC6C0000,
            0x3FE5EE02_A9240000,
            0x3FE60E32_F4478000,
            0x3FE62E42_FEFA0000,
        ]()


@always_inline("nodebug")
fn _get_log_fraction1_trail[dtype: DType]() -> FloatTable[129, dtype]:
    """Returns the table entries of `log_fraction1_trail`."""
    assert_float_dtype["dtype", dtype]()

    @parameter
    if dtype == DType.float32:
        return FloatTable[129, dtype].from_hexadecimal_values[
            0x0000_0000,
            0x3429_AC42,
            0x35A8_B0FC,
            0x368D_83EB,
            0x3726_C39E,
            0x3687_B9FF,
            0x3631_EC66,
            0x36DD_7119,
            0x35C3_0046,
            0x365B_BA8E,
            0x3757_961C,
            0x34E7_E0C3,
            0x3752_8AE5,
            0x368B_AC63,
            0x36DA_7496,
            0x36A9_1EB8,
            0x34ED_C55E,
            0x3711_31DC,
            0x377C_F9A1,
            0x36AB_54BE,
            0x3725_F040,
            0x3778_403D,
            0x36AD_B32E,
            0x374B_C743,
            0x36C1_C29E,
            0x35FE_719D,
            0x3590_210E,
            0x3681_9483,
            0x3735_39E9,
            0x36F0_7F8B,
            0x3712_9D01,
            0x354E_85B2,
            0x376F_8F35,
            0x3685_AD3F,
            0x356D_C55E,
            0x36B7_2F71,
            0x3643_6AF2,
            0x3716_52D3,
            0x3713_89CE,
            0x363F_9AE5,
            0x36E5_5D5D,
            0x36C6_0B4D,
            0x34FD_E7BD,
            0x36D0_9EF4,
            0x370A_EB84,
            0x36EC_D4C4,
            0x3645_5694,
            0x3742_1A1B,
            0x363C_21C6,
            0x36FC_ABBC,
            0x3736_1CB8,
            0x375C_5D15,
            0x3776_FD60,
            0x353C_AE72,
            0x3601_E9B1,
            0x366A_A2BA,
            0x36BF_B5DF,
            0x3715_7F75,
            0x3760_EE0B,
            0x368E_D0F4,
            0x374E_1B05,
            0x36F4_39B3,
            0x36A0_E109,
            0x36AC_08BF,
            0x370F_B2FE,
            0x3410_E5BB,
            0x3732_6A68,
            0x3724_C91E,
            0x375D_EF32,
            0x36C4_B499,
            0x3659_DA72,
            0x36BD_3E6D,
            0x375F_1E6A,
            0x373C_2E18,
            0x3779_F804,
            0x371C_9629,
            0x3728_1307,
            0x3601_A7C7,
            0x3518_75A2,
            0x36CE_9234,
            0x3675_FAF0,
            0x370F_E9C1,
            0x36C4_7BC8,
            0x3738_2160,
            0x3715_2D2F,
            0x377C_ED49,
            0x3772_E0E7,
            0x36F4_F573,
            0x3717_2129,
            0x3698_5D1D,
            0x371C_F16A,
            0x370C_AC9F,
            0x35F4_BD35,
            0x36AB_BD8A,
            0x3656_8CF9,
            0x3740_FB8A,
            0x377A_F382,
            0x3766_8C95,
            0x3706_BDF6,
            0x375E_74C8,
            0x3770_944E,
            0x373F_F616,
            0x369E_D449,
            0x3721_B720,
            0x3739_9A79,
            0x3719_C891,
            0x3689_D9CE,
            0x373D_AA3A,
            0x34D3_562A,
            0x3609_4000,
            0x359A_9C56,
            0x375C_268B,
            0x36FE_8467,
            0x377F_17CE,
            0x36BC_21C6,
            0x371E_8F54,
            0x3742_ED82,
            0x374D_7E67,
            0x3740_8DBB,
            0x371E_600B,
            0x36D2_65BC,
            0x360C_F333,
            0x374E_ADA0,
            0x36DB_5CD8,
            0x3418_6B3E,
            0x370E_E361,
            0x35AA_4906,
            0x3717_A23C,
            0x35BF_BE8E,
        ]()
    else:  # dtype == DType.float64
        return FloatTable[129, dtype].from_hexadecimal_values[
            0x00000000_00000000,
            0x3D706788_FC376904,
            0x3D7807C7_9F3DB4EA,
            0x3D76C46A_A49FD532,
            0x3D298026_7C7E09E4,
            0x3D75EAFD_480AD901,
            0x3D76E7E2_31A7950F,
            0x3D671BEC_28D14C7E,
            0x3D662A66_17CC9717,
            0x3D77AE0E_8625C174,
            0x3D7D1D09_29983761,
            0x3D383F69_278E686A,
            0x3D76B4B9_BE499B9F,
            0x3D79B20F_5ACB42A6,
            0x3D6AAC6C_A17A4554,
            0x3D7C7431_5D617EF8,
            0x3D6E5CBD_3D50FFFC,
            0x3D7EABAA_A2E519A0,
            0x3D71CB2C_D2EE2F48,
            0x3D69E80A_41811A39,
            0x3D7EA469_80BB8E20,
            0x3D71EE87_79B2D8AC,
            0x3D6F4799_F4F6543E,
            0x3D7686F4_5C803ADC,
            0x3D727A79_0E7C4141,
            0x3D7A4391_AA8ECB8A,
            0x3D7B2123_7C6D65AD,
            0x3D792212_04012030,
            0x3D65AD1D_904C1D4E,
            0x3D76AC63_26E2360F,
            0x3D77FE5B_19CC0327,
            0x3D719AC5_3F39D122,
            0x3D734435_84BB03DE,
            0x3D77722B_7221ACBF,
            0x3D7E5CBD_3D50FFFC,
            0x3D664D94_8637950E,
            0x3D5F1546_AAA3361C,
            0x3D741036_B89EF42D,
            0x3D50E35F_73F7A018,
            0x3D72B546_27E82AD3,
            0x3D630701_CE63EAB9,
            0x3D59FAFB_C68E7540,
            0x3D7E5968_90A14F6A,
            0x3D3A8D7A_D24C13F0,
            0x3D5D309C_2CC91A85,
            0x3D7F5535_9159D3FB,
            0x3D717A71_CBCD735D,
            0x3D63F1DE_86093EFA,
            0x3D77B09F_42DECDED,
            0x3D7E2836_D326527E,
            0x3D7E0B42_724F5834,
            0x3D769B15_26ADB283,
            0x3D4394A1_1B1C1EE4,
            0x3D71E54B_DBD7C8AA,
            0x3D54AEC4_42BE1015,
            0x3D60F1C6_09C98C6C,
            0x3D7401A7_389314FF,
            0x3D71E89F_057691FF,
            0x3D67E1B2_59D2F3DA,
            0x3D462E92_7628CBC2,
            0x3D7096AD_69C62044,
            0x3D78B1DF_85DA755A,
            0x3D56FABA_4CDD147D,
            0x3D7822B2_EA4BBB12,
            0x3D584BF2_B68D766F,
            0x3D6D8515_FE535B87,
            0x3D40931A_909FEA5E,
            0x3D7E1AC4_4CE11285,
            0x3D4EC519_7DDB55D3,
            0x3D50FB59_8FB14F89,
            0x3D5B7BF7_861D37AC,
            0x3D66A6B9_D9E0A5BD,
            0x3D5A21AC_25D81EF3,
            0x3D7CFADE_DF4AF2AB,
            0x3D7AF558_77B232FB,
            0x3D679F28_28ADD176,
            0x3D71282F_B989A927,
            0x3D7B0987_2F63D165,
            0x3D3E1F8D_F68DBCF3,
            0x3D49802E_B9DCA7E7,
            0x3D3BB2CD_720EC44C,
            0x3D645630_A2B61E5B,
            0x3D7CC7B6_6BECF93B,
            0x3D7D2615_65F40D93,
            0x3D60FEC6_9C695D7F,
            0x3D736932_FE608046,
            0x3D6F404E_57963891,
            0x3D7A905C_8FED4A80,
            0x3D75415B_4C4BDD9A,
            0x3D5917ED_D5CBBD2D,
            0x3D592DFB_C7D93617,
            0x3D77DE29_80918CE4,
            0x3D65E9A9_8F33A396,
            0x3D69A0BF_C60E6FA0,
            0x3D52DD98_B97BAEF0,
            0x3D7E125E_4A7C7670,
            0x3D1A07BD_8B34BE7C,
            0x3D75B6C9_A81E87BB,
            0x3D7FA141_6F1B4391,
            0x3D7BE59E_021D6D69,
            0x3D788D75_BC1F9EDD,
            0x3D784564_2E6B65D3,
            0x3D5D1772_F5386374,
            0x3D334202_A10C3491,
            0x3D60BE1F_B590A1F5,
            0x3D6D7132_0556B67B,
            0x3D70E98B_75C96C43,
            0x3D4ED717_74092113,
            0x3D750CD4_E221301B,
            0x3D7E1157_C76F60C3,
            0x3D66FAA4_04263D0B,
            0x3D77A12D_A74EA82E,
            0x3D795732_325E617A,
            0x3D5CCEF4_E4F736C2,
            0x3D6EC27D_0B7B37B3,
            0x3D51BEE7_ABD17660,
            0x3D7F5D81_13DFA3D3,
            0x3D771CD8_B4766E99,
            0x3D6BB09C_B0985646,
            0x3D7435A5_E59D2D86,
            0x3D46ABB9_DF22BC57,
            0x3D6497A9_15428B44,
            0x3D79CCA0_8E310B9B,
            0x3D7CC498_49792ECA,
            0x3D6F2CFB_29AAA5F0,
            0x3D67648C_F6E3C5D7,
            0x3D667570_D6095FD2,
            0x3D51B194_F912B417,
            0x3D7CF79A_BC9E3B3A,
        ]()


@register_passable("trivial")
struct LogConstants[dtype: DType]:
    """Provides constants for the implementation of logarithm functions.

    Set `fraction1[j] = 1 + j / 128` for `j = 0, 1, ..., 128`. We computed the
    tables `inv_fraction1`, `log_fraction1_lead`, and `log_fraction1_trail` as
    follows:

    1. Set `N = 16` for single precision, and set `N = 39` for double precision;
    2. `inv_fraction1[j] = 1 / fraction1[j]`;
    3. `log_fraction1_lead[j] = 2**(-N) * trunc(2**N * log(fraction1[j]))`; and
    4. `log_fraction1_trail[j] = log(fraction1[j]) - log_fraction1_lead[j]`.

    Parameters:
        dtype: The data type of the constants.

    Constraints:
        The parameter `dtype` must be `float32` or `float64`.
    """

    alias inv_fraction1 = _get_inv_fraction1[dtype]()
    """The reciprocal of `fraction1[j]` for `j = 0, 1, ..., 128`."""

    alias log_fraction1_lead = _get_log_fraction1_lead[dtype]()
    """The leading bits of `log(fraction1[j])` for `j = 0, 1, ..., 128`."""

    alias log_fraction1_trail = _get_log_fraction1_trail[dtype]()
    """The trailing bits of `log(fraction1[j])` for `j = 0, 1, ..., 128`."""


--- src/specials/elementary/exp.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
#
# References:
#
# Tang, P. T. P. (1989). Table-driven implementation of the exponential function
#   in IEEE floating-point arithmetic.
# ACM Transactions on Mathematical Software (TOMS), 15(2), 144-157.
# https://doi.org/10.1145/63522.214389

"""Implements the exponential function."""

import math

from memory.unsafe import bitcast

from specials._internal import math as math_lib
from specials._internal.asserting import assert_float_dtype
from specials._internal.polynomial import Polynomial
from specials.elementary.common_constants import ExpTable


@always_inline
fn _exp_impl[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width], cond: SIMD[DType.bool, simd_width]) -> SIMD[
    dtype, simd_width
]:
    """Implements the exponential function as specified in the reference paper.
    """
    var safe_x = cond.select(x, 1.0)

    var index: SIMD[DType.int32, simd_width]
    var exponent: SIMD[DType.int32, simd_width]
    var expm1_r: SIMD[dtype, simd_width]

    @parameter
    if dtype == DType.float32:
        alias inv_ln2_over_32: SIMD[dtype, simd_width] = bitcast[
            dtype, DType.uint32
        ](
            0x4238_AA3B,
        )
        alias ln2_over_32_lead: SIMD[dtype, simd_width] = bitcast[
            dtype, DType.uint32
        ](
            0x3CB1_7200,
        )
        alias ln2_over_32_trail: SIMD[dtype, simd_width] = bitcast[
            dtype, DType.uint32
        ](
            0x333F_BE8E,
        )
        alias polynomial = Polynomial[
            2, dtype, simd_width
        ].from_hexadecimal_coefficients[
            0x3F00_0044,
            0x3E2A_AAEC,
        ]()

        var xn = round(safe_x * inv_ln2_over_32)
        var xn2 = xn % 32.0
        var xn1 = xn - xn2

        var xn_is_large = (abs(xn) >= 512)
        var x_reduced_lead = math.fma(
            -xn_is_large.select(xn1, xn), ln2_over_32_lead, safe_x
        )
        x_reduced_lead = xn_is_large.select(
            math.fma(-xn2, ln2_over_32_lead, x_reduced_lead), x_reduced_lead
        )
        var x_reduced_trail = -xn * ln2_over_32_trail

        index = xn2.cast[DType.int32]()
        exponent = xn1.cast[DType.int32]() / 32

        var x_reduced = x_reduced_lead + x_reduced_trail

        expm1_r = x_reduced_lead + (
            math.fma(
                x_reduced * x_reduced, polynomial(x_reduced), x_reduced_trail
            )
        )

    else:  # dtype == DType.float64
        alias inv_ln2_over_32: SIMD[dtype, simd_width] = bitcast[
            dtype, DType.uint64
        ](
            0x40471547_652B82FE,
        )
        alias ln2_over_32_lead: SIMD[dtype, simd_width] = bitcast[
            dtype, DType.uint64
        ](
            0x3F962E42_FEF00000,
        )
        alias ln2_over_32_trail: SIMD[dtype, simd_width] = bitcast[
            dtype, DType.uint64
        ](
            0x3D8473DE_6AF278ED,
        )
        alias polynomial = Polynomial[
            5, dtype, simd_width
        ].from_hexadecimal_coefficients[
            0x3FE00000_00000000,
            0x3FC55555_55548F7C,
            0x3FA55555_55545D4E,
            0x3F811115_B7AA905E,
            0x3F56C172_8D739765,
        ]()

        var xn = round(safe_x * inv_ln2_over_32)
        var xn2 = xn % 32.0
        var xn1 = xn - xn2

        var xn_is_large = (abs(xn) >= 512)
        var x_reduced_lead = math.fma(
            -xn_is_large.select(xn1, xn), ln2_over_32_lead, safe_x
        )
        x_reduced_lead = xn_is_large.select(
            math.fma(-xn2, ln2_over_32_lead, x_reduced_lead), x_reduced_lead
        )
        var x_reduced_trail = -xn * ln2_over_32_trail

        index = xn2.cast[DType.int32]()
        exponent = xn1.cast[DType.int32]() / 32

        var x_reduced = x_reduced_lead + x_reduced_trail

        expm1_r = x_reduced_lead + (
            math.fma(
                x_reduced * x_reduced, polynomial(x_reduced), x_reduced_trail
            )
        )

    var s_lead = ExpTable[dtype].lead.unsafe_lookup(index)
    var s_trail = ExpTable[dtype].trail.unsafe_lookup(index)
    var s = s_lead + s_trail

    var mantissa = s_lead + math.fma(s, expm1_r, s_trail)

    return math_lib.ldexp(mantissa, exponent)


fn exp[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width]) -> SIMD[dtype, simd_width]:
    """Computes the exponential of `x`.

    Parameters:
        dtype: The data type of the input and output SIMD vectors.
        simd_width: The width of the input and output SIMD vectors.

    Args:
        x: A SIMD vector of floating-point values.

    Returns:
        A SIMD vector containing the exponential of `x`.

    Constraints:
        The data type must be a floating-point of single (`float32`) or double
        (`float64`) precision.
    """
    assert_float_dtype["dtype", dtype]()

    alias inf: SIMD[dtype, simd_width] = math.inf[dtype]()

    var result: SIMD[dtype, simd_width] = math.nan[dtype]()
    var x_abs = abs(x)

    # Regions of computation
    var is_in_region1: SIMD[DType.bool, simd_width]  # abs(x) < xeps
    var is_in_region2: SIMD[DType.bool, simd_width]  # x > xmax
    var is_in_region3: SIMD[DType.bool, simd_width]  # x < xmin
    var is_in_region4: SIMD[
        DType.bool, simd_width
    ]  # xmin <= x <= -xeps or xeps <= x <= xmax

    @parameter
    if dtype == DType.float32:
        alias xeps: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint32](
            0x3300_0000,
        )
        # `xmax` is different from what is specified in the reference paper:
        # `alias xmax = math.nextafter(log(FloatLimits[dtype].max()), 0.0)`
        alias xmax: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint32](
            0x42B1_7217,
        )
        alias xmin: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint32](
            0xC2CE_8ECF,
        )

        is_in_region1 = x_abs < xeps
        is_in_region2 = x > xmax
        is_in_region3 = x < xmin
        is_in_region4 = (x_abs >= xeps) & (x >= xmin) & (x <= xmax)

    else:  # dtype == DType.float64
        alias xeps: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint64](
            0x3C900000_00000000,
        )
        # `xmax` is different from what is specified in the reference paper:
        # `alias xmax = log(FloatLimits[dtype].max())`
        alias xmax: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint64](
            0x40862E42_FEFA39EF,
        )
        alias xmin: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint64](
            0xC0874385_446D71C3,
        )

        is_in_region1 = x_abs < xeps
        is_in_region2 = x > xmax
        is_in_region3 = x < xmin
        is_in_region4 = (x_abs >= xeps) & (x >= xmin) & (x <= xmax)

    result = is_in_region1.select[dtype](1.0, result)
    result = is_in_region2.select(inf, result)
    result = is_in_region3.select[dtype](0.0, result)

    if is_in_region4.reduce_or():
        result = is_in_region4.select(_exp_impl(x, is_in_region4), result)

    return result


--- src/specials/elementary/exp2.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
#
# References:
#
# Tang, P. T. P. (1989). Table-driven implementation of the exponential function
#   in IEEE floating-point arithmetic.
# ACM Transactions on Mathematical Software (TOMS), 15(2), 144-157.
# https://doi.org/10.1145/63522.214389
#
# Tang, P. T. P. (1991). Table-lookup algorithms for elementary functions and
#   their error analysis.
# Proceedings 10th IEEE Symposium on Computer Arithmetic, pp. 232-236.
# https://doi.org/10.1109/ARITH.1991.145565

"""Implements the base-2 exponential function."""

import math

from memory.unsafe import bitcast

from specials._internal import math as math_lib
from specials._internal.asserting import assert_float_dtype
from specials._internal.polynomial import Polynomial
from specials.elementary.common_constants import ExpTable


@always_inline
fn _exp2_impl[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width], cond: SIMD[DType.bool, simd_width]) -> SIMD[
    dtype, simd_width
]:
    """Implements the base-2 exponential function."""
    # Reduction: Find the breakpoint c[k] = k/32, k = 0, 1, ..., 31 such that
    #     |x - (m + c[k])| <= 1/64
    #   where m = -1, 0 or 1. Then calculate r by r = x - (m + c[k]). Note that
    #   r is in the range [-1/64, 1/64].
    # Approximation: Approximate 2^r - 1 by a polynomial p(r) whose coefficients
    #   were obtained using Sollya:
    #     > f = (2^x - 1)/x;
    #     > P = fpminimax(f, 2, [|single...|], [-1/64, 1/64]);
    #     or
    #     > P = fpminimax(f, 5, [|D...|], [-1/64, 1/64]);
    #     > P;
    #     > dirtyinfnorm(f-P, [-1/64, 1/64]);
    # Reconstruction: Reconstruct 2^x by the relationship
    #     2^x ~= 2^m * (2^c[k] + 2^c[k] * p(r))
    var safe_x = cond.select(x, 1.0)

    var index: SIMD[DType.int32, simd_width]
    var exponent: SIMD[DType.int32, simd_width]
    var exp2m1_r: SIMD[dtype, simd_width]

    @parameter
    if dtype == DType.float32:
        alias one_over_32: SIMD[dtype, simd_width] = bitcast[
            dtype, DType.uint32
        ](
            0x3D00_0000,
        )
        alias polynomial = Polynomial[
            3, dtype, simd_width
        ].from_hexadecimal_coefficients[
            0x3F31_7218,
            0x3E75_FE66,
            0x3D63_4D8A,
        ]()

        var xn = round(safe_x)
        var xf = safe_x - xn

        var yn = round(xf * 32.0)
        var yn2 = yn % 32.0
        var yn1 = yn - yn2

        var y_reduced = math.fma(-yn, one_over_32, xf)

        index = yn2.cast[DType.int32]()
        exponent = (xn + yn1 / 32).cast[DType.int32]()

        exp2m1_r = y_reduced * polynomial(y_reduced)

    else:  # dtype == DType.float64
        alias one_over_32: SIMD[dtype, simd_width] = bitcast[
            dtype, DType.uint64
        ](
            0x3FA00000_00000000,
        )
        alias polynomial = Polynomial[
            6, dtype, simd_width
        ].from_hexadecimal_coefficients[
            0x3FE62E42_FEFA39EF,
            0x3FCEBFBD_FF82C58E,
            0x3FAC6B08_D70496BC,
            0x3F83B2AB_6FBCFDA6,
            0x3F55D884_2A55CA01,
            0x3F24308B_04A657CB,
        ]()

        var xn = round(safe_x)
        var xf = safe_x - xn

        var yn = round(xf * 32.0)
        var yn2 = yn % 32.0
        var yn1 = yn - yn2

        var y_reduced = math.fma(-yn, one_over_32, xf)

        index = yn2.cast[DType.int32]()
        exponent = (xn + yn1 / 32).cast[DType.int32]()

        exp2m1_r = y_reduced * polynomial(y_reduced)

    var s_lead = ExpTable[dtype].lead.unsafe_lookup(index)
    var s_trail = ExpTable[dtype].trail.unsafe_lookup(index)
    var s = s_lead + s_trail

    var mantissa = s_lead + math.fma(s, exp2m1_r, s_trail)

    return math_lib.ldexp(mantissa, exponent)


fn exp2[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width]) -> SIMD[dtype, simd_width]:
    """Computes the base-2 exponential of `x`.

    Parameters:
        dtype: The data type of the input and output SIMD vectors.
        simd_width: The width of the input and output SIMD vectors.

    Args:
        x: A SIMD vector of floating-point values.

    Returns:
        A SIMD vector containing the base-2 exponential of `x`.

    Constraints:
        The data type must be a floating-point of single (`float32`) or double
        (`float64`) precision.
    """
    assert_float_dtype["dtype", dtype]()

    alias inf: SIMD[dtype, simd_width] = math.inf[dtype]()

    var result: SIMD[dtype, simd_width] = math.nan[dtype]()
    var x_abs = abs(x)

    # Regions of computation
    var is_in_region1: SIMD[DType.bool, simd_width]  # abs(x) < xeps
    var is_in_region2: SIMD[DType.bool, simd_width]  # x > xmax
    var is_in_region3: SIMD[DType.bool, simd_width]  # x < xmin
    var is_in_region4: SIMD[
        DType.bool, simd_width
    ]  # xmin <= x <= -xeps or xeps <= x <= xmax

    @parameter
    if dtype == DType.float32:
        alias xeps: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint32](
            0x3300_0000,
        )
        # `xmax` is different from what is specified in the reference paper:
        # `alias xmax = math.nextafter(Float32(FloatLimits[dtype].max_exponent), 0.0)`
        alias xmax: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint32](
            0x42FF_FFFF,
        )
        alias xmin: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint32](
            0xC315_0000,
        )

        is_in_region1 = x_abs < xeps
        is_in_region2 = x > xmax
        is_in_region3 = x < xmin
        is_in_region4 = (x_abs >= xeps) & (x >= xmin) & (x <= xmax)

    else:  # dtype == DType.float64
        alias xeps: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint64](
            0x3C900000_00000000,
        )
        # `xmax` is different from what is specified in the reference paper:
        # `alias xmax = math.nextafter(Float64(FloatLimits[dtype].max_exponent), 0.0)`
        alias xmax: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint64](
            0x408FFFFF_FFFFFFFF,
        )
        alias xmin: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint64](
            0xC090C800_00000000,
        )

        is_in_region1 = x_abs < xeps
        is_in_region2 = x > xmax
        is_in_region3 = x < xmin
        is_in_region4 = (x_abs >= xeps) & (x >= xmin) & (x <= xmax)

    result = is_in_region1.select[dtype](1.0, result)
    result = is_in_region2.select(inf, result)
    result = is_in_region3.select[dtype](0.0, result)

    if is_in_region4.reduce_or():
        result = is_in_region4.select(_exp2_impl(x, is_in_region4), result)

    return result


--- src/specials/elementary/expm1.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
#
# References:
#
# Tang, P. T. P. (1992). Table-driven implementation of the Expm1 function in
#   IEEE floating-point arithmetic.
# ACM Transactions on Mathematical Software (TOMS), 18(2), 211-222.
# https://doi.org/10.1145/146847.146928

"""Implements the `expm1` function."""

import math

from memory.unsafe import bitcast

from specials._internal import math as math_lib
from specials._internal.asserting import assert_float_dtype
from specials._internal.polynomial import Polynomial
from specials.elementary.common_constants import ExpTable


@always_inline
fn _expm1_procedure_1[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width], cond: SIMD[DType.bool, simd_width]) -> SIMD[
    dtype, simd_width
]:
    """Implements the procedure 1 of `expm1` as specified in the reference paper.
    """
    var safe_x = cond.select(x, 1.0)

    var index: SIMD[DType.int32, simd_width]
    var exponent: SIMD[DType.int32, simd_width]
    var expm1_r: SIMD[dtype, simd_width]
    var precision_minus_1: SIMD[DType.int32, simd_width]

    @parameter
    if dtype == DType.float32:
        alias inv_ln2_over_32: SIMD[dtype, simd_width] = bitcast[
            dtype, DType.uint32
        ](
            0x4238_AA3B,
        )
        alias ln2_over_32_lead: SIMD[dtype, simd_width] = bitcast[
            dtype, DType.uint32
        ](
            0x3CB1_7200,
        )
        alias ln2_over_32_trail: SIMD[dtype, simd_width] = bitcast[
            dtype, DType.uint32
        ](
            0x333F_BE8E,
        )
        alias polynomial = Polynomial[
            2, dtype, simd_width
        ].from_hexadecimal_coefficients[
            0x3F00_0044,
            0x3E2A_AAEC,
        ]()

        var xn = round(safe_x * inv_ln2_over_32)
        var xn2 = xn % 32.0
        var xn1 = xn - xn2

        var xn_is_large = (abs(xn) >= 512)
        var x_reduced_lead = math.fma(
            -xn_is_large.select(xn1, xn), ln2_over_32_lead, safe_x
        )
        x_reduced_lead = xn_is_large.select(
            math.fma(-xn2, ln2_over_32_lead, x_reduced_lead), x_reduced_lead
        )
        var x_reduced_trail = -xn * ln2_over_32_trail

        index = xn2.cast[DType.int32]()
        exponent = xn1.cast[DType.int32]() / 32

        var x_reduced = x_reduced_lead + x_reduced_trail

        expm1_r = x_reduced_lead + (
            math.fma(
                x_reduced * x_reduced, polynomial(x_reduced), x_reduced_trail
            )
        )
        precision_minus_1 = 23  # 24 - 1

    else:  # dtype == DType.float64
        alias inv_ln2_over_32: SIMD[dtype, simd_width] = bitcast[
            dtype, DType.uint64
        ](
            0x40471547_652B82FE,
        )
        alias ln2_over_32_lead: SIMD[dtype, simd_width] = bitcast[
            dtype, DType.uint64
        ](
            0x3F962E42_FEF00000,
        )
        alias ln2_over_32_trail: SIMD[dtype, simd_width] = bitcast[
            dtype, DType.uint64
        ](
            0x3D8473DE_6AF278ED,
        )
        alias polynomial = Polynomial[
            5, dtype, simd_width
        ].from_hexadecimal_coefficients[
            0x3FE00000_00000000,
            0x3FC55555_55548F7C,
            0x3FA55555_55545D4E,
            0x3F811115_B7AA905E,
            0x3F56C172_8D739765,
        ]()

        var xn = round(safe_x * inv_ln2_over_32)
        var xn2 = xn % 32.0
        var xn1 = xn - xn2

        var xn_is_large = (abs(xn) >= 512)
        var x_reduced_lead = math.fma(
            -xn_is_large.select(xn1, xn), ln2_over_32_lead, safe_x
        )
        x_reduced_lead = xn_is_large.select(
            math.fma(-xn2, ln2_over_32_lead, x_reduced_lead), x_reduced_lead
        )
        var x_reduced_trail = -xn * ln2_over_32_trail

        index = xn2.cast[DType.int32]()
        exponent = xn1.cast[DType.int32]() / 32

        var x_reduced = x_reduced_lead + x_reduced_trail

        expm1_r = x_reduced_lead + (
            math.fma(
                x_reduced * x_reduced, polynomial(x_reduced), x_reduced_trail
            )
        )
        precision_minus_1 = 52  # 53 - 1

    var inv_exp2 = math.ldexp[dtype, simd_width](0.25, 2 - exponent)
    var s_lead = ExpTable[dtype].lead.unsafe_lookup(index)
    var s_trail = ExpTable[dtype].trail.unsafe_lookup(index)
    var s = s_lead + s_trail

    var mantissa = (s_lead - inv_exp2) + math.fma(
        s_lead, expm1_r, s_trail * (1.0 + expm1_r)
    )
    mantissa = (exponent > precision_minus_1).select(
        s_lead + math.fma(s, expm1_r, s_trail - inv_exp2),
        mantissa,
    )

    var exponent_is_too_negative = (exponent <= -8.0)
    mantissa = exponent_is_too_negative.select(
        s_lead + math.fma(s, expm1_r, s_trail),
        mantissa,
    )

    var result = math_lib.ldexp(mantissa, exponent)
    result = exponent_is_too_negative.select(result - 1.0, result)

    return result


@always_inline
fn _expm1_procedure_2[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width], cond: SIMD[DType.bool, simd_width]) -> SIMD[
    dtype, simd_width
]:
    """Implements the procedure 2 of `expm1` as specified in the reference paper.
    """
    var safe_x = cond.select(x, 0.1)
    var x_exp2: SIMD[dtype, simd_width]
    var x3_gval: SIMD[dtype, simd_width]

    @parameter
    if dtype == DType.float32:
        alias exp2 = math.ldexp(Scalar[dtype](1.0), 16)
        x_exp2 = safe_x * exp2

        alias g = Polynomial[
            5, dtype, simd_width
        ].from_hexadecimal_coefficients[
            0x3E2A_AAAA,
            0x3D2A_AAA0,
            0x3C08_89FF,
            0x3AB6_4DE5,
            0x394A_B327,
        ]()
        x3_gval = safe_x * safe_x * safe_x * g(safe_x)

    else:  # dtype == DType.float64
        alias exp2 = math.ldexp(Scalar[dtype](1.0), 30)
        x_exp2 = safe_x * exp2

        alias g = Polynomial[
            9, dtype, simd_width
        ].from_hexadecimal_coefficients[
            0x3FC55555_55555549,
            0x3FA55555_555554B6,
            0x3F811111_1111A9F3,
            0x3F56C16C_16CE14C6,
            0x3F2A01A0_1159DD2D,
            0x3EFA019F_635825C4,
            0x3EC71E14_BFE3DB59,
            0x3E928295_484734EA,
            0x3E5A2836_AA646B96,
        ]()

        x3_gval = safe_x * safe_x * safe_x * g(safe_x)

    # x == x_term1 + x_term2
    var x_term1 = (x_exp2 + safe_x) - x_exp2
    var x_term2 = safe_x - x_term1

    # x * x * 0.5 == x2_half_term1 + x2_half_term2
    var x2_half_term1 = x_term1 * x_term1 * 0.5
    var x2_half_term2 = x_term2 * (safe_x + x_term1) * 0.5

    return (x2_half_term1 < 0.0078125).select(
        safe_x + (x2_half_term1 + (x3_gval + x2_half_term2)),
        (x_term1 + x2_half_term1) + (x3_gval + (x_term2 + x2_half_term2)),
    )


fn expm1[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width]) -> SIMD[dtype, simd_width]:
    """Computes `exp(x) - 1` in a numerically stable way.

    This function is semantically equivalent to `exp(x) - 1`, but it is more
    accurate for `x` close to zero.

    Parameters:
        dtype: The data type of the input and output SIMD vectors.
        simd_width: The width of the input and output SIMD vectors.

    Args:
        x: A SIMD vector of floating-point values.

    Returns:
        A SIMD vector containing the expression `exp(x) - 1` evaluated at `x`.

    Constraints:
        The data type must be a floating-point of single (`float32`) or double
        (`float64`) precision.
    """
    assert_float_dtype["dtype", dtype]()

    alias inf: SIMD[dtype, simd_width] = math.inf[dtype]()

    var result: SIMD[dtype, simd_width] = math.nan[dtype]()
    var x_abs = abs(x)

    # Regions of computation
    var is_in_region1: SIMD[DType.bool, simd_width]  # Em1_Tiny | Em1_Zero
    var is_in_region2: SIMD[DType.bool, simd_width]  # Em1_Pos | Em1_+Inf
    var is_in_region3: SIMD[DType.bool, simd_width]  # Em1_Neg | Em1_-Inf
    var is_in_region4: SIMD[DType.bool, simd_width]  # T_1 < x < T_2
    var is_in_region5: SIMD[
        DType.bool, simd_width
    ]  # T- <= x <= T_1 | T_2 <= x <= T+

    @parameter
    if dtype == DType.float32:
        alias xeps: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint32](
            0x3300_0000,
        )
        alias xsml_inf: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint32](
            0xBE93_4B11,
        )
        alias xsml_sup: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint32](
            0x3E64_7FBF,
        )
        alias xmin: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint32](
            0xC18A_A122,
        )
        # `xmax` is different from what is specified in the reference paper:
        # `alias xmax = math.nextafter(log(FloatLimits[dtype].max()), 0.0)`
        alias xmax: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint32](
            0x42B1_7217,
        )

        is_in_region1 = x_abs < xeps
        is_in_region2 = x > xmax
        is_in_region3 = x < xmin
        is_in_region4 = ~is_in_region1 & (x > xsml_inf) & (x < xsml_sup)
        is_in_region5 = ((x >= xmin) & (x <= xsml_inf)) | (
            (x >= xsml_sup) & (x <= xmax)
        )

    else:  # dtype == DType.float64
        alias xeps: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint64](
            0x3C900000_00000000,
        )
        alias xsml_inf: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint64](
            0xBFD26962_1134DB93,
        )
        alias xsml_sup: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint64](
            0x3FCC8FF7_C79A9A22,
        )
        alias xmin: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint64](
            0xC042B708_872320E1,
        )
        # `xmax` is different from what is specified in the reference paper:
        # `alias xmax = log(FloatLimits[dtype].max())`
        alias xmax: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint64](
            0x40862E42_FEFA39EF,
        )

        is_in_region1 = x_abs < xeps
        is_in_region2 = x > xmax
        is_in_region3 = x < xmin
        is_in_region4 = ~is_in_region1 & (x > xsml_inf) & (x < xsml_sup)
        is_in_region5 = ((x >= xmin) & (x <= xsml_inf)) | (
            (x >= xsml_sup) & (x <= xmax)
        )

    result = is_in_region1.select(x, result)
    result = is_in_region2.select(inf, result)
    result = is_in_region3.select[dtype](-1.0, result)

    if is_in_region4.reduce_or():
        result = is_in_region4.select(
            _expm1_procedure_2(x, is_in_region4), result
        )

    if is_in_region5.reduce_or():
        result = is_in_region5.select(
            _expm1_procedure_1(x, is_in_region5), result
        )

    return result


--- src/specials/elementary/log.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
#
# References:
#
# Tang, P. T. P. (1990). Table-driven implementation of the logarithm function
#   in IEEE floating-point arithmetic.
# ACM Transactions on Mathematical Software (TOMS), 16(4), 378-400.
# https://doi.org/10.1145/98267.98294

"""Implements the logarithm function."""

import math

from memory.unsafe import bitcast

from specials._internal.asserting import assert_float_dtype
from specials._internal.math import ldexp
from specials._internal.polynomial import Polynomial
from specials.elementary.common_constants import LogConstants
from specials.utils.numerics import FloatLimits


# TODO: Try to further improve the performance of the `log` function.


@always_inline
fn _log_procedure_1[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width], cond: SIMD[DType.bool, simd_width]) -> SIMD[
    dtype, simd_width
]:
    """Implements the procedure 1 of `log` as specified in the reference paper.
    """
    var safe_x = cond.select(x, 1.0)

    var fraction_and_exponent = math.frexp(safe_x)
    var fraction = 2.0 * fraction_and_exponent[0]
    var exponent = fraction_and_exponent[1] - 1

    var fraction1 = ldexp(round(ldexp(fraction, 7)), -7)
    var fraction2 = fraction - fraction1
    var index = round(ldexp(fraction1 - 1.0, 7)).cast[DType.int32]()

    var log2_lead = LogConstants[dtype].log_fraction1_lead.get[128]()
    var log2_trail = LogConstants[dtype].log_fraction1_trail.get[128]()

    var result1 = math.fma[dtype, simd_width](
        exponent,
        log2_lead,
        LogConstants[dtype].log_fraction1_lead.unsafe_lookup(index),
    )
    var result2 = math.fma[dtype, simd_width](
        exponent,
        log2_trail,
        LogConstants[dtype].log_fraction1_trail.unsafe_lookup(index),
    )

    var u = fraction2 * LogConstants[dtype].inv_fraction1.unsafe_lookup(index)
    var u_squared_times_pval: SIMD[dtype, simd_width]

    # We use Sollya to find a polynomial p such that p(u) best approximates the
    # function f(u) = (log1p(u) - u)/u**2 on the given interval:
    #   > min_value = -2^(-8);
    #   > max_value = -min_value;
    #   > f = (log1p(u) - u)/u^2;
    #   > P = fpminimax(f, 1, [|single...|], [min_value, max_value]);
    #   or
    #   > P = fpminimax(f, 4, [|D...|], [min_value, max_value]);
    #   > P;
    #   > dirtyinfnorm(f-P, [min_value, max_value]);

    @parameter
    if dtype == DType.float32:
        alias p = Polynomial[
            2, dtype, simd_width
        ].from_hexadecimal_coefficients[
            0xBF00_0020,
            0x3EAA_AAE6,
        ]()
        u_squared_times_pval = u * u * p(u)

    else:  # dtype == DType.float64
        alias p = Polynomial[
            5, dtype, simd_width
        ].from_hexadecimal_coefficients[
            0xBFE00000_00000000,
            0x3FD55555_555279E5,
            0xBFCFFFFF_FFFA0C2B,
            0x3FC999B0_7518C512,
            0xBFC5556A_79F895CB,
        ]()
        u_squared_times_pval = u * u * p(u)

    result2 = u + (u_squared_times_pval + result2)

    return result1 + result2


@always_inline
fn _log_procedure_2[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width], cond: SIMD[DType.bool, simd_width]) -> SIMD[
    dtype, simd_width
]:
    """Implements the procedure 2 of `log` as specified in the reference paper.
    """
    var y = cond.select(x - 1.0, 1.0)
    var inv_y_plus_two = 1.0 / (y + 2.0)
    var u = 2.0 * y * inv_y_plus_two
    var u_squared = u * u

    var u_cubed_times_pval: SIMD[dtype, simd_width]
    var precision_shift: Int

    # We use Sollya to find an even polynomial p such that p(u) best approximates
    # (log1p(2*u/(2 - u)) - u)/u**3 on the given interval:
    #   > min_value = (2 - 2*exp(1/16))/(1 + exp(1/16));
    #   > max_value = -min_value;
    #   > f = (log1p(2*u/(2 - u)) - u)/u^3;
    #   > P = fpminimax(f, [|0,2|], [|single...|], [min_value, max_value]);
    #   or
    #   > P = fpminimax(f, [|0,2,4,6|], [|D...|], [min_value, max_value]);
    #   > P;
    #   > dirtyinfnorm(f-P, [min_value, max_value]);

    @parameter
    if dtype == DType.float32:
        alias p = Polynomial[
            2, dtype, simd_width
        ].from_hexadecimal_coefficients[
            0x3DAA_AAAA,
            0x3C4C_F264,
        ]()
        u_cubed_times_pval = u * u_squared * p(u_squared)

        # precision_shift: 24 - 12 + 1
        precision_shift = 13

    else:  # dtype == DType.float64
        alias p = Polynomial[
            4, dtype, simd_width
        ].from_hexadecimal_coefficients[
            0x3FB55555_5555554A,
            0x3F899999_99A528F3,
            0x3F624923_AA1832F2,
            0x3F3C7D68_D285CA94,
        ]()

        u_cubed_times_pval = u * u_squared * p(u_squared)

        # precision_shift: 53 - 24 + 1
        precision_shift = 30

    # (u == u_term1 + u_term2) and (y == y_term1 + y_term2)
    var u_precision_scale = ldexp(u, precision_shift)
    var u_term1 = (u_precision_scale + u) - u_precision_scale
    var y_precision_scale = ldexp(y, precision_shift)
    var y_term1 = (y_precision_scale + y) - y_precision_scale
    var y_term2 = y - y_term1
    var u_term2 = inv_y_plus_two * (
        math.fma(
            -u_term1, y_term2, math.fma(-u_term1, y_term1, 2.0 * (y - u_term1))
        )
    )

    return u_term1 + (u_term2 + u_cubed_times_pval)


fn log[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width]) -> SIMD[dtype, simd_width]:
    """Computes the natural logarithm of `x`.

    Parameters:
        dtype: The data type of the input and output SIMD vectors.
        simd_width: The width of the input and output SIMD vectors.

    Args:
        x: A SIMD vector of floating-point values.

    Returns:
        A SIMD vector containing the natural logarithm of `x`.

    Constraints:
        The data type must be a floating-point of single (`float32`) or double
        (`float64`) precision.
    """
    assert_float_dtype["dtype", dtype]()

    alias inf: SIMD[dtype, simd_width] = math.inf[dtype]()
    alias log2: SIMD[dtype, simd_width] = 0.6931471805599453
    alias digits: SIMD[dtype, simd_width] = FloatLimits[dtype].digits
    alias xmin: SIMD[dtype, simd_width] = FloatLimits[dtype].min()

    var result: SIMD[dtype, simd_width] = math.nan[dtype]()
    var x_abs = abs(x)

    # Regions of computation
    var is_in_region1 = (x == inf)
    var is_in_region2 = (x_abs == 0.0)
    var is_in_region3 = (x == 1.0)
    var is_in_region4: SIMD[
        DType.bool, simd_width
    ]  # (x != 1.0) & (x > xone_inf) & (x < xone_sup)
    var is_in_region5: SIMD[DType.bool, simd_width] = (x > 0.0) & (x < xmin)
    var is_in_region6: SIMD[
        DType.bool, simd_width
    ]  # ((x >= xmin) & (x <= xone_inf)) | ((x >= xone_sup) & (x < inf))

    @parameter
    if dtype == DType.float32:
        alias xone_inf: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint32](
            0x3F70_7D5F,
        )
        alias xone_sup: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint32](
            0x3F88_415B,
        )

        is_in_region4 = (x != 1.0) & (x > xone_inf) & (x < xone_sup)
        is_in_region6 = ((x >= xmin) & (x <= xone_inf)) | (
            (x >= xone_sup) & (x < inf)
        )

    else:  # dtype == DType.float64
        alias xone_inf: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint64](
            0x3FEE0FAB_FBC702A3,
        )
        alias xone_sup: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint64](
            0x3FF1082B_577D34EE,
        )

        is_in_region4 = (x != 1.0) & (x > xone_inf) & (x < xone_sup)
        is_in_region6 = ((x >= xmin) & (x <= xone_inf)) | (
            (x >= xone_sup) & (x < inf)
        )

    result = is_in_region1.select(x, result)
    result = is_in_region2.select(-inf, result)
    result = is_in_region3.select[dtype](0.0, result)

    # TODO: Should we avoid creating runtime branches to be accelerator friendly?
    if is_in_region4.reduce_or():
        result = is_in_region4.select(
            _log_procedure_2(x, is_in_region4), result
        )

    if is_in_region5.reduce_or():
        # For handling subnormal numbers, we use the following identity:
        #   log(x) = -scale * log(2) + log(x * 2**scale)

        var scale = is_in_region5.select(digits - 1, 0.0)
        result = is_in_region5.select(
            math.fma(
                -scale,
                log2,
                _log_procedure_1(
                    math.ldexp(x, scale.cast[DType.int32]()), is_in_region5
                ),
            ),
            result,
        )

    if is_in_region6.reduce_or():
        result = is_in_region6.select(
            _log_procedure_1(x, is_in_region6), result
        )

    return result


--- src/specials/elementary/log1p.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
#
# References:
#
# Tang, P. T. P. (1990). Table-driven implementation of the logarithm function
#   in IEEE floating-point arithmetic.
# ACM Transactions on Mathematical Software (TOMS), 16(4), 378-400.
# https://doi.org/10.1145/98267.98294

"""Implements the `log1p` function."""

import math

from memory.unsafe import bitcast

from specials._internal.asserting import assert_float_dtype
from specials._internal.math import ldexp
from specials._internal.polynomial import Polynomial
from specials.elementary.common_constants import LogConstants
from specials.utils.numerics import FloatLimits


@always_inline
fn _log1p_procedure_1[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width], cond: SIMD[DType.bool, simd_width]) -> SIMD[
    dtype, simd_width
]:
    """Implements the procedure 1 of `log1p` as specified in the reference paper.
    """
    alias max_exponent = FloatLimits[dtype].max_exponent - 1
    alias significant_bits = FloatLimits[dtype].digits
    alias threshold = 2 ** (significant_bits + 2)

    var safe_x = cond.select(x, 1.0)
    var y = (safe_x < threshold).select(1.0 + safe_x, safe_x)

    var fraction_and_exponent = math.frexp(y)
    var fraction = 2.0 * fraction_and_exponent[0]
    var exponent = fraction_and_exponent[1] - 1

    var fraction1 = ldexp(round(ldexp(fraction, 7)), -7)
    var index = round(ldexp(fraction1 - 1.0, 7)).cast[DType.int32]()

    var power_of_two = ldexp[dtype, simd_width](
        1.0, -exponent.cast[DType.int32]()
    )
    var x_times_power_of_two = safe_x * power_of_two
    var fraction2 = ((exponent <= -2) | (exponent >= max_exponent)).select(
        fraction - fraction1,
        (exponent >= significant_bits).select(
            (x_times_power_of_two - fraction1) + power_of_two,
            (power_of_two - fraction1) + x_times_power_of_two,
        ),
    )

    var log2_lead = LogConstants[dtype].log_fraction1_lead.get[128]()
    var log2_trail = LogConstants[dtype].log_fraction1_trail.get[128]()

    var result1 = math.fma[dtype, simd_width](
        exponent,
        log2_lead,
        LogConstants[dtype].log_fraction1_lead.unsafe_lookup(index),
    )
    var result2 = math.fma[dtype, simd_width](
        exponent,
        log2_trail,
        LogConstants[dtype].log_fraction1_trail.unsafe_lookup(index),
    )

    var u = fraction2 * LogConstants[dtype].inv_fraction1.unsafe_lookup(index)
    var u_squared_times_pval: SIMD[dtype, simd_width]

    # We use Sollya to find a polynomial p such that p(u) best approximates the
    # function f(u) = (log1p(u) - u)/u**2 on the given interval:
    #   > min_value = -2^(-8);
    #   > max_value = -min_value;
    #   > f = (log1p(u) - u)/u^2;
    #   > P = fpminimax(f, 1, [|single...|], [min_value, max_value]);
    #   or
    #   > P = fpminimax(f, 4, [|D...|], [min_value, max_value]);
    #   > P;
    #   > dirtyinfnorm(f-P, [min_value, max_value]);

    @parameter
    if dtype == DType.float32:
        alias p = Polynomial[
            2, dtype, simd_width
        ].from_hexadecimal_coefficients[
            0xBF00_0020,
            0x3EAA_AAE6,
        ]()
        u_squared_times_pval = u * u * p(u)

    else:  # dtype == DType.float64
        alias p = Polynomial[
            5, dtype, simd_width
        ].from_hexadecimal_coefficients[
            0xBFE00000_00000000,
            0x3FD55555_555279E5,
            0xBFCFFFFF_FFFA0C2B,
            0x3FC999B0_7518C512,
            0xBFC5556A_79F895CB,
        ]()
        u_squared_times_pval = u * u * p(u)

    result2 = u + (u_squared_times_pval + result2)

    return result1 + result2


@always_inline
fn _log1p_procedure_2[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width], cond: SIMD[DType.bool, simd_width]) -> SIMD[
    dtype, simd_width
]:
    """Implements the procedure 2 of `log1p` as specified in the reference paper.
    """
    var safe_x = cond.select(x, 0.0)
    var inv_x_plus_two = 1.0 / (safe_x + 2.0)
    var u = 2.0 * safe_x * inv_x_plus_two
    var u_squared = u * u

    var u_cubed_times_pval: SIMD[dtype, simd_width]
    var precision_shift: Int

    # We use Sollya to find an even polynomial p such that p(u) best approximates
    # (log1p(2*u/(2 - u)) - u)/u**3 on the given interval:
    #   > min_value = (2 - 2*exp(1/16))/(1 + exp(1/16));
    #   > max_value = -min_value;
    #   > f = (log1p(2*u/(2 - u)) - u)/u^3;
    #   > P = fpminimax(f, [|0,2|], [|single...|], [min_value, max_value]);
    #   or
    #   > P = fpminimax(f, [|0,2,4,6|], [|D...|], [min_value, max_value]);
    #   > P;
    #   > dirtyinfnorm(f-P, [min_value, max_value]);

    @parameter
    if dtype == DType.float32:
        alias p = Polynomial[
            2, dtype, simd_width
        ].from_hexadecimal_coefficients[
            0x3DAA_AAAA,
            0x3C4C_F264,
        ]()
        u_cubed_times_pval = u * u_squared * p(u_squared)

        # precision_shift: 24 - 12 + 1
        precision_shift = 13

    else:  # dtype == DType.float64
        alias p = Polynomial[
            4, dtype, simd_width
        ].from_hexadecimal_coefficients[
            0x3FB55555_5555554A,
            0x3F899999_99A528F3,
            0x3F624923_AA1832F2,
            0x3F3C7D68_D285CA94,
        ]()

        u_cubed_times_pval = u * u_squared * p(u_squared)

        # precision_shift: 53 - 24 + 1
        precision_shift = 30

    # (u == u_term1 + u_term2) and (x == x_term1 + x_term2)
    var u_precision_scale = ldexp(u, precision_shift)
    var u_term1 = (u_precision_scale + u) - u_precision_scale
    var x_precision_scale = ldexp(safe_x, precision_shift)
    var x_term1 = (x_precision_scale + safe_x) - x_precision_scale
    var x_term2 = safe_x - x_term1
    var u_term2 = inv_x_plus_two * (
        math.fma(
            -u_term1,
            x_term2,
            math.fma(-u_term1, x_term1, 2.0 * (safe_x - u_term1)),
        )
    )

    return u_term1 + (u_term2 + u_cubed_times_pval)


@always_inline
fn _log1p_procedure_3[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width], cond: SIMD[DType.bool, simd_width]) -> SIMD[
    dtype, simd_width
]:
    """Implements the procedure of `log1p` for when `x` is tiny."""
    alias smallest_subnormal = FloatLimits[dtype].denorm_min()
    var safe_x = cond.select(x, 0.5 * FloatLimits[dtype].epsilon_neg())

    return 0.125 * math.fma[dtype, simd_width](8.0, safe_x, -smallest_subnormal)


fn log1p[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width]) -> SIMD[dtype, simd_width]:
    """Computes `log(1 + x)` in a numerically stable way.

    This function is semantically equivalent to `log(1 + x)`, but it is more
    accurate for `x` close to zero.

    Parameters:
        dtype: The data type of the input and output SIMD vectors.
        simd_width: The width of the input and output SIMD vectors.

    Args:
        x: A SIMD vector of floating-point values.

    Returns:
        A SIMD vector containing the expression `log(1 + x)` evaluated at `x`.

    Constraints:
        The data type must be a floating-point of single (`float32`) or double
        (`float64`) precision.
    """
    assert_float_dtype["dtype", dtype]()

    alias epsneg = FloatLimits[dtype].epsilon_neg()
    alias inf: SIMD[dtype, simd_width] = math.inf[dtype]()

    var result: SIMD[dtype, simd_width] = math.nan[dtype]()
    var x_abs = abs(x)

    # Regions of computation
    var is_in_region1 = (x == inf)
    var is_in_region2 = (x_abs == 0.0)
    var is_in_region3 = (x == -1.0)
    var is_in_region4 = (x_abs != 0.0) & (x_abs < epsneg)
    var is_in_region5: SIMD[
        DType.bool, simd_width
    ]  # (x_abs >= epsneg) & (x > xsml_inf) & (x < xsml_sup)
    var is_in_region6: SIMD[
        DType.bool, simd_width
    ]  # ((x > -1.0) & (x <= xsml_inf)) | ((x >= xsml_sup) & (x < inf))

    @parameter
    if dtype == DType.float32:
        alias xsml_inf: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint32](
            0xBD78_2A03,
        )
        alias xsml_sup: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint32](
            0x3D84_15AC,
        )

        is_in_region5 = (x_abs >= epsneg) & (x > xsml_inf) & (x < xsml_sup)
        is_in_region6 = ((x > -1.0) & (x <= xsml_inf)) | (
            (x >= xsml_sup) & (x < inf)
        )

    else:  # dtype == DType.float64
        alias xsml_inf: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint64](
            0xBFAF0540_438FD5C4,
        )
        alias xsml_sup: SIMD[dtype, simd_width] = bitcast[dtype, DType.uint64](
            0x3FB082B5_77D34ED8,
        )

        is_in_region5 = (x_abs >= epsneg) & (x > xsml_inf) & (x < xsml_sup)
        is_in_region6 = ((x > -1.0) & (x <= xsml_inf)) | (
            (x >= xsml_sup) & (x < inf)
        )

    result = (is_in_region1 | is_in_region2).select(x, result)
    result = is_in_region3.select(-inf, result)

    # TODO: Should we avoid creating runtime branches to be accelerator friendly?
    if is_in_region4.reduce_or():
        result = is_in_region4.select(
            _log1p_procedure_3(x, is_in_region4), result
        )

    if is_in_region5.reduce_or():
        result = is_in_region5.select(
            _log1p_procedure_2(x, is_in_region5), result
        )

    if is_in_region6.reduce_or():
        result = is_in_region6.select(
            _log1p_procedure_1(x, is_in_region6), result
        )

    return result


--- src/specials/gamma.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2023 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
#
# Some of the code in this file is adapted from:
#
# slatec/fnlib (https://www.netlib.org/slatec/fnlib):
# Public-domain software. No copyright restrictions.
#
# References:
#
# Didonato, A. R., & Morris Jr, A. H. (1992). Algorithm 708: Significant digit
#   computation of the incomplete beta function ratios.
# ACM Transactions on Mathematical Software (TOMS), 18(3), 360-373.
# https://dl.acm.org/doi/abs/10.1145/131766.131776

"""Implements gamma-related functions."""

import math
import testing

from specials._internal import asserting
from specials._internal.polynomial import Chebyshev, Polynomial
from specials.elementary.log import log
from specials.utils.numerics import FloatLimits


fn lbeta[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width], y: SIMD[dtype, simd_width]) -> SIMD[
    dtype, simd_width
]:
    """Computes the natural logarithm of the beta function.

    This function is semantically equivalent to `lgamma(x) + lgamma(y) - lgamma(x + y)`,
    but it is more accurate for arguments greater than or equal to `8.0`.

    Parameters:
        dtype: The data type of the input and output SIMD vectors.
        simd_width: The width of the input and output SIMD vectors.

    Args:
        x: SIMD vector of non-negative floating-point values.
        y: SIMD vector of non-negative floating-point values.

    Returns:
        SIMD vector containing the natural logarithm of the beta function.

    Constraints:
        The data type must be a floating-point of single (`float32`) or double
        (`float64`) precision.
    """
    asserting.assert_float_dtype["dtype", dtype]()

    alias inf: SIMD[dtype, simd_width] = math.inf[dtype]()
    alias nan: SIMD[dtype, simd_width] = math.nan[dtype]()
    alias log_sqrt_2pi: SIMD[
        dtype, simd_width
    ] = 0.91893853320467274178032973640562

    # Ensure that `a` is the smaller of the two arguments and `b` is the larger one.
    # Although the Beta function is mathematically symmetric, this procedure is not.
    var a = min(x, y)
    var b = max(x, y)

    # The `math.lgamma`` operation is one of the most computationally expensive
    # operations in this procedure. To avoid calling it when possible, we mask out
    # large values of `a` and `b`.
    var a_small = (a < 8.0).select(a, nan)
    var b_small = (b < 8.0).select(b, nan)

    var lgamma_a_small = math.lgamma(a_small)
    var apb = a + b
    var a_over_apb = a / apb
    var log1p_neg_a_over_apb = math.log1p(-a_over_apb)

    # `a` and `b` are small: `a <= b < 8.0`.
    var result = lgamma_a_small + math.lgamma(b_small) - math.lgamma(
        a_small + b_small
    )

    # `a` is small, but `b` is large: `a < 8.0 <= b`.
    var correction = lgamma_correction(b) - lgamma_correction(apb)
    var result_for_large_b = (
        lgamma_a_small
        + correction
        + a
        - a * log(apb)
        + (b - 0.5) * log1p_neg_a_over_apb
    )
    result = (b >= 8.0).select(result_for_large_b, result)

    # `a` and `b` are large: `8.0 <= a <= b`.
    correction += lgamma_correction(a)
    var result_for_large_a = (
        -0.5 * log(b)
        + log_sqrt_2pi
        + correction
        + (a - 0.5) * log(a_over_apb)
        + b * log1p_neg_a_over_apb
    )
    result = (a >= 8.0).select(result_for_large_a, result)

    # We have already computed the value of the log-beta function for positive arguments.
    # For other cases, this procedure returns the same values as the corresponding one in
    # the R language.
    return ((a < 0.0) | math.isnan(x) | math.isnan(y)).select(
        nan, (a == 0.0).select(inf, math.isinf(b).select(-inf, result))
    )


fn lgamma_correction[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width]) -> SIMD[dtype, simd_width]:
    """Computes the correction term for the Rocktaeschel's approximation of `lgamma`.

    The correction term is defined as:

    `lgamma_correction(x) = lgamma(x) - (x - 0.5) * log(x) + x - 0.5 * log(2 * pi)`

    for `x >= 8`.

    Parameters:
        dtype: The data type of the input and output SIMD vectors.
        simd_width: The width of the input and output SIMD vectors.

    Args:
        x: SIMD vector of floating-point values greater than or equal to `8.0`.

    Returns:
        SIMD vector containing the correction term for the Rocktaeschel's approximation
        of `lgamma`. If `x` is less than `8.0`, this function returns `NaN`.

    Constraints:
        The data type must be a floating-point of single (`float32`) or double
        (`float64`) precision.
    """
    asserting.assert_float_dtype["dtype", dtype]()

    alias nan: SIMD[dtype, simd_width] = math.nan[dtype]()
    alias zero: SIMD[dtype, simd_width] = 0.0

    alias xmin: SIMD[dtype, simd_width] = 8.0
    alias xbig: SIMD[dtype, simd_width] = 1.0 / (
        math.exp2[dtype, 1](0.5 * -FloatLimits[dtype].digits)
    )
    alias xmax: SIMD[dtype, simd_width] = 1.0 / (
        12.0 * FloatLimits[dtype].min()
    )

    # The coefficients for the Chebyshev approximation of this correction were obtained
    # using the Python library `mpmath`.
    alias p = Chebyshev[20, dtype, simd_width].from_coefficients[
        8.331170390906488010133812318436e-02,
        -2.16055508054460412844538843806e-05,
        2.380513030666125633967836809341e-08,
        -6.79698274141255339847704839014e-11,
        3.598298746801337252645642260689e-13,
        -3.00664186830727299934841316608e-15,
        3.600735976941671612063259733719e-17,
        -5.79169539359268115708965063230e-19,
        1.193723234577627766675645918486e-20,
        -3.04227079051491795027184994902e-22,
        9.322841959636230355753533026206e-24,
        -3.35893572063489139361118640771e-25,
        1.396918849726567542569514379524e-26,
        -6.60413093595761811539762816839e-28,
        3.503829528301945510964075209918e-29,
        -2.06344897851678280985454179987e-30,
        1.336227216567260503733730549409e-31,
        -9.43764302155440072371370822355e-33,
        7.218203478875394218977123232114e-34,
        -5.89320165797572035218853002751e-35,
    ]()
    alias error_tolerance = 0.1 * FloatLimits[dtype].epsilon_neg()
    alias num_terms = p.economize[error_tolerance]()
    alias p_truncated = p.truncate[num_terms]()

    return ((x < xmin) | math.isnan(x)).select(
        nan,
        (x < xbig).select(
            p_truncated(2.0 * pow(xmin / x, 2) - 1.0) / x,
            (x < xmax).select(1.0 / (12.0 * x), zero),
        ),
    )


fn lgamma1p[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width]) -> SIMD[dtype, simd_width]:
    """Computes `lgamma(1 + x)` in a numerically stable way.

    This function is semantically equivalent to `lgamma(1 + x)`, but it is more
    accurate for `x` close to zero.

    Parameters:
        dtype: The data type of the input and output SIMD vectors.
        simd_width: The width of the input and output SIMD vectors.

    Args:
        x: SIMD vector of floating-point values.

    Returns:
        SIMD vector containing the expression `lgamma(1 + x)` evaluated at `x`.

    Constraints:
        The data type must be a floating-point of single (`float32`) or double
        (`float64`) precision.
    """
    asserting.assert_float_dtype["dtype", dtype]()

    alias nan: SIMD[dtype, simd_width] = math.nan[dtype]()
    var result: SIMD[dtype, simd_width] = nan

    # Regions of computation.
    var is_in_region1 = (x >= -0.2) & (x < 0.6)
    var is_in_region2 = (x >= 0.6) & (x <= 1.25)
    var is_in_region3 = ~math.isnan(x) & ~is_in_region1 & ~is_in_region2

    # Polynomials for region 1. The coefficients for the Padé approximation were
    # obtained using the Python library `mpmath`.
    alias p = Polynomial[10, dtype, simd_width].from_coefficients[
        5.772156649015328606065120900824e-1,
        1.769751426777699103134469694093e-0,
        1.571904140511368034267480819223e-0,
        -4.57882665358839512689779140447e-1,
        -1.72712505786380004829886606981e-0,
        -1.24373712528022745342232844850e-0,
        -4.17229580597323137925159852465e-1,
        -6.80978370725741258151865551687e-2,
        -4.71020922504118253059534042963e-3,
        -8.87567923452439608685161841459e-5,
    ]()
    alias q = Polynomial[10, dtype, simd_width].from_coefficients[
        1.000000000000000000000000000000e-0,
        4.490901092651424325538968592651e-0,
        8.428109112438682661243930563021e-0,
        8.567162656125254544979174422045e-0,
        5.110442815300870959225621274210e-0,
        1.811088008784189174050238153628e-0,
        3.678104258279395409229240536674e-1,
        3.891333138124453879127500000527e-2,
        1.741014553601874329848935439309e-3,
        1.896441997532694197492403697806e-5,
    ]()

    # Polynomials for region 2. The coefficients for the Padé approximation were
    # obtained using the Python library `mpmath`.
    alias r = Polynomial[8, dtype, simd_width].from_coefficients[
        4.227843350984671393934879099176e-1,
        1.050000850494737509155499279591e-0,
        9.812533673494664828746361809635e-1,
        4.486129361904137782151622525624e-1,
        1.066177232215367809039427008258e-1,
        1.267871740982719010450401143716e-2,
        6.461232819244555998963476071186e-4,
        9.044855054775925733727539415320e-6,
    ]()
    alias s = Polynomial[8, dtype, simd_width].from_coefficients[
        1.000000000000000000000000000000e-0,
        1.720815452874289951756729496983e-0,
        1.167733459492857090468456899665e-0,
        3.958932481495390425060318675588e-1,
        6.995177647341877884678121833272e-2,
        6.082671403258376707307085732028e-3,
        2.160591173994851665996054475890e-4,
        1.832407275230925220950146383070e-6,
    ]()

    if is_in_region1.reduce_or():
        result = is_in_region1.select(-x * (p(x) / q(x)), result)

    if is_in_region2.reduce_or():
        var y = (x - 0.5) - 0.5
        result = is_in_region2.select(y * (r(y) / s(y)), result)

    if is_in_region3.reduce_or():
        var z = is_in_region3.select(1.0 + x, nan)
        result = is_in_region3.select(math.lgamma(z), result)

    return result


fn rgamma1pm1[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width]) -> SIMD[dtype, simd_width]:
    """Computes `1 / gamma(1 + x) - 1` in a numerically stable way.

    This function is semantically equivalent to `1 / gamma(1 + x) - 1`, but it
    is more accurate for `x` close to zero or one.

    Parameters:
        dtype: The data type of the input and output SIMD vectors.
        simd_width: The width of the input and output SIMD vectors.

    Args:
        x: SIMD vector of floating-point values.

    Returns:
        SIMD vector containing the expression `1 / gamma(1 + x) - 1` evaluated
        at `x`.

    Constraints:
        The data type must be a floating-point of single (`float32`) or double
        (`float64`) precision.
    """
    asserting.assert_float_dtype["dtype", dtype]()

    alias nan: SIMD[dtype, simd_width] = math.nan[dtype]()
    var result: SIMD[dtype, simd_width] = nan

    # Regions of computation.
    var is_in_region1 = (x == 0.0) | (x == 1.0)
    var is_in_region2 = (x >= -0.5) & (x < 0.0)
    var is_in_region3 = (x > 0.0) & (x <= 0.5)
    var is_in_region4 = (x > 0.5) & (x < 1.0)
    var is_in_region5 = (x > 1.0) & (x <= 1.5)
    var is_in_region6 = (x < -0.5) | (x > 1.5)

    # Polynomials for regions 2 and 4. The coefficients for the Padé approximation
    # were obtained using the Python library `mpmath`.
    alias p = Polynomial[10, dtype, simd_width].from_coefficients[
        -4.22784335098467139393487909918e-1,
        -8.76243643973193958120640666347e-1,
        -4.59653437436261810715536535224e-1,
        1.253267646667917761310767750400e-2,
        1.272374059074339062508590520139e-2,
        -5.95722659095617453307017824897e-3,
        3.070451110948726727765078685413e-4,
        2.297364646087461210880646489337e-4,
        -2.92867644133341610115726150281e-5,
        2.184035804013220749396991885951e-6,
    ]()
    alias q = Polynomial[10, dtype, simd_width].from_coefficients[
        1.000000000000000000000000000000e-0,
        5.212245444278738169713276344712e-1,
        1.792664653862777325772960453280e-1,
        3.438203782078653915730104663393e-2,
        4.263280285377850240586205099463e-3,
        -1.05916724442728169202533807166e-4,
        -1.22076484585335162669853056235e-4,
        -3.17838959035037282903233187238e-5,
        -3.65081153946647239430275797323e-6,
        -3.52809435523569771837732680697e-7,
    ]()

    # Polynomials for regions 3 and 5. The coefficients for the Padé approximation
    # were obtained using the Python library `mpmath`.
    alias r = Polynomial[10, dtype, simd_width].from_coefficients[
        5.772156649015328606065120900824e-1,
        -3.55019099545320141149313031876e-1,
        -2.80386972049984078138240489896e-1,
        4.691471428746571677040872413793e-2,
        1.698702087612124086567211030085e-2,
        -6.06314331539890270227271205613e-3,
        1.849686265095375101066548123063e-4,
        1.979525687052423927977413302098e-4,
        -3.29375759528006334058753730013e-5,
        1.831226368489650977559259205254e-6,
    ]()
    alias s = q

    result = is_in_region1.select[dtype](0.0, result)

    var t = (is_in_region2 | is_in_region3).select(x, x - 1.0)

    if (is_in_region2 | is_in_region4).reduce_or():
        var y = p(t) / q(t)
        result = is_in_region2.select(
            x * (y + 1.0), is_in_region4.select((t / x) * y, result)
        )

    if (is_in_region3 | is_in_region5).reduce_or():
        var z = r(t) / s(t)
        result = is_in_region3.select(
            x * z, is_in_region5.select((t / x) * (z - 1.0), result)
        )

    if is_in_region6.reduce_or():
        result = is_in_region6.select(1.0 / math.gamma(1.0 + x) - 1.0, result)

    return result


--- src/specials/utils/__init__.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #

"""Provides utilities for use within the package."""


--- src/specials/utils/big_int.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
#
# Some of the code in this file is adapted from:
#
# llvm/llvm-project:
# Licensed under the Apache License v2.0 with LLVM Exceptions.

"""Implements the `BigInt` struct."""

from bit import countl_zero
from memory import DTypePointer, memset_zero
from sys.info import is_64bit, sizeof
from sys.intrinsics import _RegisterPackType
from utils import InlineArray
from utils.numerics import max_finite


# ===----------------------------------------------------------------------=== #
# Operations with carry propagation
# ===----------------------------------------------------------------------=== #


@always_inline
fn _uadd_with_overflow(
    lhs: SIMD, rhs: __type_of(lhs)
) -> (__type_of(lhs), __type_of(lhs)):
    """Performs unsigned addition with overflow detection."""
    constrained[
        lhs.type.is_unsigned(),
        "argument type must be an unsigned, integral type",
    ]()

    var result = llvm_intrinsic[
        "llvm.uadd.with.overflow",
        _RegisterPackType[__type_of(lhs), SIMD[DType.bool, lhs.size]],
        __type_of(lhs),
        __type_of(lhs),
    ](lhs, rhs)

    return result[0], result[1].cast[lhs.type]()


@always_inline
fn _uadd_with_carry(
    lhs: SIMD, rhs: __type_of(lhs), carry_in: __type_of(lhs)
) -> (__type_of(lhs), __type_of(lhs)):
    """Performs unsigned addition with carry propagation."""
    constrained[
        lhs.type.is_unsigned(),
        "argument type must be an unsigned, integral type",
    ]()

    var sum_and_carry0 = _uadd_with_overflow(lhs, rhs)
    var sum_and_carry1 = _uadd_with_overflow(sum_and_carry0[0], carry_in)
    var carry_out = sum_and_carry0[1] | sum_and_carry1[1]

    return sum_and_carry1[0], carry_out


@always_inline
fn _usub_with_overflow(
    lhs: SIMD, rhs: __type_of(lhs)
) -> (__type_of(lhs), __type_of(lhs)):
    """Performs unsigned subtraction with overflow detection."""
    constrained[
        lhs.type.is_unsigned(),
        "argument type must be an unsigned, integral type",
    ]()

    var result = llvm_intrinsic[
        "llvm.usub.with.overflow",
        _RegisterPackType[__type_of(lhs), SIMD[DType.bool, lhs.size]],
        __type_of(lhs),
        __type_of(lhs),
    ](lhs, rhs)

    return result[0], result[1].cast[lhs.type]()


@always_inline
fn _usub_with_carry(
    lhs: SIMD, rhs: __type_of(lhs), carry_in: __type_of(lhs)
) -> (__type_of(lhs), __type_of(lhs)):
    """Performs subtraction with carry propagation."""
    constrained[
        lhs.type.is_unsigned(),
        "argument type must be an unsigned, integral type",
    ]()

    var diff_and_carry0 = _usub_with_overflow(lhs, rhs)
    var diff_and_carry1 = _usub_with_overflow(diff_and_carry0[0], carry_in)
    var carry_out = diff_and_carry0[1] | diff_and_carry1[1]

    return diff_and_carry1[0], carry_out


@always_inline
fn _inplace_binop[
    binop_with_carry: fn[type: DType, size: Int] (
        SIMD[type, size], SIMD[type, size], SIMD[type, size]
    ) -> (SIMD[type, size], SIMD[type, size]),
    type: DType,
    size: Int,
](
    inout dst: InlineArray[SIMD[type, size], _],
    rhs: InlineArray[SIMD[type, size], _],
) -> SIMD[type, size]:
    """Performs an in-place binary operation with carry propagation."""
    constrained[
        dst.size >= rhs.size,
        "`dst` must have at least as many elements as `rhs`",
    ]()

    var carry_out = SIMD[type, size](0)

    @parameter
    for i in range(dst.size):
        var has_rhs_value = i < rhs.size
        var rhs_value = rhs[i] if has_rhs_value else 0
        var carry_in = carry_out
        var result_and_carry = binop_with_carry(dst[i], rhs_value, carry_in)

        dst[i] = result_and_carry[0]
        carry_out = result_and_carry[1]

        # Stop early when rhs is over and there is no carry out to propagate.
        if all(carry_out == 0) and not has_rhs_value:
            break

    return carry_out


# ===----------------------------------------------------------------------=== #
# Operations for bit manipulation
# ===----------------------------------------------------------------------=== #


@always_inline
fn _count_leading_zeros[
    type: DType, size: Int
](val: InlineArray[SIMD[type, size], _]) -> SIMD[type, size]:
    """Counts the leading zeros in the internal representation of a `BigInt`."""
    constrained[type.is_integral(), "type must be an integral type"]()

    alias TYPE_BITWIDTH = type.bitwidth()

    var result = SIMD[type, size](0)
    var should_stop = SIMD[DType.bool, size](False)

    @parameter
    for i in reversed(range(val.size)):
        var bit_count = countl_zero(val[i])
        result += should_stop.select(0, bit_count)
        should_stop |= bit_count < TYPE_BITWIDTH

        if all(should_stop):
            break

    return result


@always_inline
fn _mask_leading_ones[type: DType, count: Int]() -> Scalar[type]:
    """Creates a mask with the specified number of leading ones."""
    return ~_mask_trailing_ones[type, type.bitwidth() - count]()


@always_inline
fn _mask_trailing_ones[type: DType, count: Int]() -> Scalar[type]:
    """Creates a mask with the specified number of trailing ones."""
    constrained[type.is_unsigned(), "type must be an unsigned, integral type"]()
    constrained[count >= 0, "count must be non-negative"]()
    constrained[
        count <= type.bitwidth(),
        "count must be less than or equal to the type's bitwidth",
    ]()

    @parameter
    if count == 0:
        return Scalar[type](0)
    else:
        return ~Scalar[type](0) >> (type.bitwidth() - count)


@always_inline
fn _iota[type: DType, size: Int]() -> SIMD[type, size]:
    """Creates a SIMD vector containing an increasing sequence starting from 0.
    """
    # Unlike `math.iota`, this function can be executed at compile-time.
    var result = SIMD[type, size]()

    @parameter
    for i in range(size):
        result[i] = i

    return result


@always_inline
fn _shift[
    *, is_left_shift: Bool
](val: BigInt, offset: SIMD[DType.index, val.size],) -> __type_of(val):
    """Performs a bitwise shift on the internal representation of a `BigInt`."""
    alias SHIFT = _iota[DType.index, val.size]()
    alias TYPE_BITWIDTH = val.word_type.bitwidth()

    var dst = __type_of(val)(unsafe_uninitialized=True)
    var val_ptr = DTypePointer(
        val._storage.unsafe_ptr().bitcast[Scalar[val.word_type]]()
    )

    if all(offset == 0):

        @parameter
        for i in range(val.WORD_COUNT):
            dst._storage[i] = val._storage[i]

        return dst

    @always_inline
    @parameter
    fn at(index: SIMD[DType.index, _]) -> __type_of(index):
        @parameter
        if is_left_shift:
            return val.WORD_COUNT - index - 1
        else:
            return index

    @always_inline
    @parameter
    fn safe_gather(
        index: SIMD[DType.index, val.size]
    ) -> SIMD[val.word_type, val.size]:
        var is_index_below_size = index < val.WORD_COUNT
        var mask = (index >= 0) & is_index_below_size
        var default = (val.is_negative() & ~is_index_below_size).select(
            SIMD[val.word_type, val.size](-1), 0
        )

        return val_ptr.gather(index.fma(val.size, SHIFT), mask, default)

    var index_offset = offset // TYPE_BITWIDTH
    var bit_offset = (offset % TYPE_BITWIDTH).cast[val.word_type]()

    @parameter
    for i in range(val.WORD_COUNT):
        var index = Scalar[DType.index](i)
        var part1 = safe_gather(at(index + index_offset))
        var part2 = safe_gather(at(index + index_offset + 1))

        @parameter
        if is_left_shift:
            dst._storage[int(at(index))] = (bit_offset == 0).select(
                part1,
                part1 << bit_offset | part2 >> (TYPE_BITWIDTH - bit_offset),
            )
        else:
            dst._storage[int(at(index))] = (bit_offset == 0).select(
                part1,
                part1 >> bit_offset | part2 << (TYPE_BITWIDTH - bit_offset),
            )

    return dst


# ===----------------------------------------------------------------------=== #
# Operations for comparison
# ===----------------------------------------------------------------------=== #


@always_inline
fn _compare(lhs: SIMD, rhs: __type_of(lhs)) -> SIMD[DType.int8, lhs.size]:
    """Compares two SIMD vectors element-wise."""
    return (lhs == rhs).select(
        0, (lhs < rhs).select(-1, SIMD[DType.int8, lhs.size](1))
    )


@always_inline
fn _compare(lhs: BigInt, rhs: __type_of(lhs)) -> SIMD[DType.int8, lhs.size]:
    """Compares two `BigInt` vectors element-wise."""
    var result = SIMD[DType.int8, lhs.size](0)

    @parameter
    if lhs.signed:
        var lhs_is_negative = lhs.is_negative()
        var rhs_is_negative = rhs.is_negative()
        result = (lhs_is_negative != rhs_is_negative).select(
            lhs_is_negative.select(-1, SIMD[DType.int8, lhs.size](1)), result
        )

    @parameter
    for i in reversed(range(lhs.WORD_COUNT)):
        result = (result == 0).select(
            _compare(lhs._storage[i], rhs._storage[i]), result
        )

        if all(result != 0):
            break

    return result


# ===----------------------------------------------------------------------=== #
# BigInt
# ===----------------------------------------------------------------------=== #


@always_inline
fn _conditional[T: AnyType, //, pred: Bool, true_case: T, false_case: T]() -> T:
    """Returns the true or false case based on the value of `pred`."""

    @parameter
    if pred:
        return true_case
    else:
        return false_case


@always_inline
fn _default_word_type[bits: Int]() -> DType:
    """Returns the default word type for a `BigInt` based on `bits`."""
    constrained[bits > 0, "number of bits must be positive"]()
    constrained[bits % 8 == 0, "number of bits must be a multiple of 8"]()

    @parameter
    if bits % 64 == 0 and is_64bit():
        return DType.uint64
    elif bits % 32 == 0:
        return DType.uint32
    elif bits % 16 == 0:
        return DType.uint16
    else:
        return DType.uint8


@always_inline
fn _big_int_construction_checks[
    bits: Int,
    word_type: DType,
]():
    """Performs checks on the parameters of a `BigInt` constructor."""
    constrained[bits > 0, "number of bits must be positive"]()
    constrained[
        word_type.is_unsigned(), "word type must be an unsigned, integral type"
    ]()
    constrained[
        bits % word_type.bitwidth() == 0,
        "number of bits must be a multiple of the word type's bitwidth",
    ]()


@always_inline
fn _is_casting_safe[bits: Int, signed: Bool](value: IntLiteral) -> Bool:
    """Checks if `value` fits in an integer with the specified number of bits
    and signedness.
    """
    constrained[bits > 0, "number of bits must be positive"]()

    @parameter
    if signed:
        return bits >= value._bit_width()
    else:
        return value >= 0 and bits >= (value._bit_width() - 1)


alias BigUInt = BigInt[_, size=_, signed=False, word_type=_]
"""Represents a small vector of arbitrary, fixed bit-size unsigned integers."""


@value
struct BigInt[
    bits: Int,
    /,
    *,
    size: Int,
    signed: Bool = True,
    word_type: DType = _default_word_type[bits](),
](Copyable, ExplicitlyCopyable, Movable):
    """Represents a small vector of arbitrary, fixed bit-size integers.

    It can represent both signed and unsigned integers with a fixed number of
    bits each and leverages SIMD operations for enhanced performance.

    Constraints:
        The number of bits must be a multiple of the word type's bitwidth.

    Parameters:
        bits: The number of bits for each element of the `BigInt` vector.
            Constraints: Must be positive.
        size: The size of the `BigInt` vector. Constraints: Must be positive
            and a power of two.
        signed: A boolean indicating whether the integers are signed (`True`)
            or unsigned (`False`). Defaults to `True`.
        word_type: The type of each word used in the internal representation
            of the `BigInt` vector. For performance reasons, defaults to the
            largest unsigned integer type whose bitwidth divides `bits` evenly.
    """

    # ===------------------------------------------------------------------=== #
    # Aliases
    # ===------------------------------------------------------------------=== #

    alias WORD_TYPE_BITWIDTH = 8 * sizeof[word_type]()
    """The size, in bits, of the `word_type` parameter."""

    alias WORD_COUNT = bits // Self.WORD_TYPE_BITWIDTH
    """The number of words used to represent the integers."""

    alias StorageType = InlineArray[SIMD[word_type, size], Self.WORD_COUNT]
    """The type used for internal storage, represented as an array of words."""

    # ===------------------------------------------------------------------=== #
    # Fields
    # ===------------------------------------------------------------------=== #

    var _storage: Self.StorageType
    """The internal array of words representing the `BigInt` vector."""

    # ===------------------------------------------------------------------=== #
    # Life cycle methods
    # ===------------------------------------------------------------------=== #

    @always_inline
    fn __init__(inout self):
        """Initializes the `BigInt` vector with all elements set to zero."""
        _big_int_construction_checks[bits, word_type]()

        alias BLOCK_SIZE = size * Self.WORD_COUNT

        self._storage = Self.StorageType(unsafe_uninitialized=True)

        memset_zero[word_type](
            self._storage.unsafe_ptr().bitcast[Scalar[word_type]](), BLOCK_SIZE
        )

    @always_inline
    fn __init__(inout self, *, unsafe_uninitialized: Bool):
        """Initializes the `BigInt` vector with uninitialized storage.

        Args:
            unsafe_uninitialized: A boolean indicating whether the internal
                storage should be left uninitialized. In practice, it is always
                set to `True` (it is not actually used inside the constructor).
        """
        _big_int_construction_checks[bits, word_type]()

        _ = unsafe_uninitialized
        self._storage = Self.StorageType(unsafe_uninitialized=True)

    @always_inline
    fn __init__(inout self, value: IntLiteral):
        """Initializes the `BigInt` vector with a signed integer literal.

        Args:
            value: The signed integer literal to be splatted across all the
                elements of the `BigInt` vector.
        """
        _big_int_construction_checks[bits, word_type]()

        debug_assert(
            _is_casting_safe[bits, signed](value),
            "value must be within the bounds of the `BigInt`",
        )

        self._storage = Self.StorageType(unsafe_uninitialized=True)
        var tmp: IntLiteral = value

        @parameter
        for i in range(Self.WORD_COUNT):
            self._storage[i] = SIMD[word_type, size](tmp)
            tmp >>= Self.WORD_TYPE_BITWIDTH

    @always_inline
    fn __init__(inout self, value: Int):
        """Initializes the `BigInt` vector with the provided integer value.

        Args:
            value: The integer value to set for each element in the `BigInt`
                vector.
        """
        self.__init__(SIMD[DType.index, size](value))

    @always_inline
    fn __init__(inout self, value: SIMD[_, size]):
        """Initializes the `BigInt` vector with the provided SIMD vector.

        Constraints:
            The value type must be an integral type.

        Args:
            value: The SIMD vector to initialize the `BigInt` vector with.
        """
        constrained[
            value.type.is_integral(),
            "value type must be an integral type",
        ]()
        _big_int_construction_checks[bits, word_type]()

        alias TYPE_BITWIDTH = value.type.bitwidth()

        var extension = ((value < 0) & signed).select(
            max_finite[word_type](), 0
        )
        var tmp = value

        self._storage = Self.StorageType(unsafe_uninitialized=True)

        @parameter
        for i in range(Self.WORD_COUNT):
            self._storage[i] = (tmp == 0).select(
                extension, tmp.cast[word_type]()
            )

            @parameter
            if TYPE_BITWIDTH > Self.WORD_TYPE_BITWIDTH:
                tmp >>= Self.WORD_TYPE_BITWIDTH
            else:
                tmp = 0

    @always_inline
    fn __init__(inout self, other: Self):
        """Initializes a new `BigInt` vector by copying an existing `BigInt`.

        Args:
            other: The `BigInt` vector to copy from.
        """
        self.__copyinit__(other)

    # ===------------------------------------------------------------------=== #
    # Factory methods
    # ===------------------------------------------------------------------=== #

    @staticmethod
    @always_inline
    fn max() -> Self:
        """Creates a `BigInt` vector with all elements set to the maximum
        representable value.

        Returns:
            A new `BigInt` vector with all elements set to the maximum
            representable value.
        """
        var result = ~Self()

        @parameter
        if signed:
            result.clear_most_significant_bit()

        return result

    @staticmethod
    @always_inline
    fn min() -> Self:
        """Creates a `BigInt` vector with all elements set to the minimum
        representable value.

        Returns:
            A new `BigInt` vector with all elements set to the minimum
            representable value.
        """
        var result = Self()

        @parameter
        if signed:
            result.set_most_significant_bit()

        return result

    # ===------------------------------------------------------------------=== #
    # Operator dunders
    # ===------------------------------------------------------------------=== #

    @always_inline
    fn __add__(self, rhs: Self) -> Self:
        """Performs addition between two `BigInt` vectors, element-wise.

        Args:
            rhs: The right-hand side `BigInt` vector.

        Returns:
            A new `BigInt` vector containing the result of the addition.
        """
        var result = Self(self)

        _ = _inplace_binop[_uadd_with_carry](result._storage, rhs._storage)

        return result

    @always_inline
    fn __iadd__(inout self, rhs: Self):
        """Performs in-place addition between two `BigInt` vectors, element-wise.

        Args:
            rhs: The right-hand side `BigInt` vector.
        """
        _ = _inplace_binop[_uadd_with_carry](self._storage, rhs._storage)

    @always_inline
    fn __sub__(self, rhs: Self) -> Self:
        """Performs subtraction between two `BigInt` vectors, element-wise.

        Args:
            rhs: The right-hand side `BigInt` vector.

        Returns:
            A new `BigInt` vector containing the result of the subtraction.
        """
        var result = Self(self)

        _ = _inplace_binop[_usub_with_carry](result._storage, rhs._storage)

        return result

    @always_inline
    fn __isub__(inout self, rhs: Self):
        """Performs in-place subtraction between two `BigInt` vectors,
        element-wise.

        Args:
            rhs: The right-hand side `BigInt` vector.
        """
        _ = _inplace_binop[_usub_with_carry](self._storage, rhs._storage)

    @always_inline
    fn __eq__(self, rhs: Self) -> SIMD[DType.bool, size]:
        """Compares two `BigInt` vectors for equality, element-wise.

        Args:
            rhs: The right-hand side `BigInt` vector.

        Returns:
            A SIMD vector of booleans indicating whether the corresponding
            elements are equal.
        """
        return _compare(self, rhs) == 0

    @always_inline
    fn __ne__(self, rhs: Self) -> SIMD[DType.bool, size]:
        """Compares two `BigInt` vectors for inequality, element-wise.

        Args:
            rhs: The right-hand side `BigInt` vector.

        Returns:
            A SIMD vector of booleans indicating whether the corresponding
            elements are not equal.
        """
        return _compare(self, rhs) != 0

    @always_inline
    fn __lt__(self, rhs: Self) -> SIMD[DType.bool, size]:
        """Compares two `BigInt` vectors for less-than, element-wise.

        Args:
            rhs: The right-hand side `BigInt` vector.

        Returns:
            A SIMD vector of booleans indicating whether the corresponding
            elements are less than those of the right-hand side.
        """
        return _compare(self, rhs) == -1

    @always_inline
    fn __le__(self, rhs: Self) -> SIMD[DType.bool, size]:
        """Compares two `BigInt` vectors for less-than-or-equal, element-wise.

        Args:
            rhs: The right-hand side `BigInt` vector.

        Returns:
            A SIMD vector of booleans indicating whether the corresponding
            elements are less than or equal to those of the right-hand side.
        """
        return _compare(self, rhs) != 1

    @always_inline
    fn __gt__(self, rhs: Self) -> SIMD[DType.bool, size]:
        """Compares two `BigInt` vectors for greater-than, element-wise.

        Args:
            rhs: The right-hand side `BigInt` vector.

        Returns:
            A SIMD vector of booleans indicating whether the corresponding
            elements are greater than those of the right-hand side.
        """
        return _compare(self, rhs) == 1

    @always_inline
    fn __ge__(self, rhs: Self) -> SIMD[DType.bool, size]:
        """Compares two `BigInt` vectors for greater-than-or-equal, element-wise.

        Args:
            rhs: The right-hand side `BigInt` vector.

        Returns:
            A SIMD vector of booleans indicating whether the corresponding
            elements are greater than or equal to those of the right-hand side.
        """
        return _compare(self, rhs) != -1

    @always_inline
    fn __invert__(self) -> Self:
        """Performs a bitwise NOT operation on a `BigInt` vector, element-wise.

        Returns:
            A new `BigInt` vector containing the result of the bitwise NOT
            operation.
        """
        var result = Self(unsafe_uninitialized=True)

        @parameter
        for i in range(Self.WORD_COUNT):
            result._storage[i] = ~self._storage[i]

        return result

    @always_inline
    fn __lshift__(self, offset: SIMD[DType.index, size]) -> Self:
        """Performs a bitwise left shift on a `BigInt` vector, element-wise.

        Args:
            offset: The number of bits to shift the vector by. Must be less than
                `bits`; otherwise, the behavior of this method is undefined.

        Returns:
            A new `BigInt` vector containing the result of the bitwise left
            shift.
        """
        debug_assert(
            all((offset >= 0) & (offset < bits)),
            "offset must be within bounds",
        )

        return _shift[is_left_shift=True](self, offset)

    @always_inline
    fn __ilshift__(inout self, offset: SIMD[DType.index, size]):
        """Performs an in-place bitwise left shift on a `BigInt` vector,
        element-wise.

        Args:
            offset: The number of bits to shift the vector by. Must be less than
                `bits`; otherwise, the behavior of this method is undefined.
        """
        self = self << offset

    @always_inline
    fn __rshift__(self, offset: SIMD[DType.index, size]) -> Self:
        """Performs a bitwise right shift on a `BigInt` vector, element-wise.

        Args:
            offset: The number of bits to shift the vector by. Must be less than
                `bits`; otherwise, the behavior of this method is undefined.

        Returns:
            A new `BigInt` vector containing the result of the bitwise right
            shift.
        """
        debug_assert(
            all((offset >= 0) & (offset < bits)),
            "offset must be within bounds",
        )

        return _shift[is_left_shift=False](self, offset)

    @always_inline
    fn __irshift__(inout self, offset: SIMD[DType.index, size]):
        """Performs an in-place bitwise right shift on a `BigInt` vector,
        element-wise.

        Args:
            offset: The number of bits to shift the vector by. Must be less than
                `bits`; otherwise, the behavior of this method is undefined.
        """
        self = self >> offset

    @always_inline
    fn __neg__(self) -> Self:
        """Performs arithmetic negation on a `BigInt` vector, element-wise.

        Returns:
            A new `BigInt` vector representing the result of the negation.
        """
        var result = ~self
        result += 1
        return result

    @always_inline
    fn __pos__(self) -> Self:
        """Performs the unary plus operation on a `BigInt` vector, element-wise.

        Returns:
            A new `BigInt` vector that is identical to the original.
        """
        return Self(self)

    # ===------------------------------------------------------------------=== #
    # Methods
    # ===------------------------------------------------------------------=== #

    @always_inline
    fn add_with_overflow(inout self, rhs: Self) -> SIMD[DType.bool, size]:
        """Performs in-place addition with overflow detection, element-wise.

        Args:
            rhs: The right-hand side `BigInt` vector.

        Returns:
            A SIMD vector of booleans indicating whether an overflow occurred
            during the addition.
        """

        @parameter
        if signed:
            var lhs_msb = self.get_most_significant_bit()
            var rhs_msb = rhs.get_most_significant_bit()

            _ = _inplace_binop[_uadd_with_carry](self._storage, rhs._storage)

            return (lhs_msb == rhs_msb) & (
                lhs_msb != self.get_most_significant_bit()
            )
        else:
            var carry_out = _inplace_binop[_uadd_with_carry](
                self._storage, rhs._storage
            )
            return carry_out.cast[DType.bool]()

    @always_inline
    fn sub_with_overflow(inout self, rhs: Self) -> SIMD[DType.bool, size]:
        """Performs in-place subtraction with overflow detection, element-wise.

        Args:
            rhs: The right-hand side `BigInt` vector.

        Returns:
            A SIMD vector of booleans indicating whether an overflow occurred
            during the subtraction.
        """

        @parameter
        if signed:
            var lhs_msb = self.get_most_significant_bit()
            var rhs_msb = rhs.get_most_significant_bit()

            _ = _inplace_binop[_usub_with_carry](self._storage, rhs._storage)

            return (lhs_msb != rhs_msb) & (
                lhs_msb != self.get_most_significant_bit()
            )
        else:
            var carry_out = _inplace_binop[_usub_with_carry](
                self._storage, rhs._storage
            )
            return carry_out.cast[DType.bool]()

    @always_inline
    fn cast[
        bits: Int, /, *, signed: Bool
    ](self) -> BigInt[
        bits, size = Self.size, signed=signed, word_type = Self.word_type
    ]:
        """Casts the `BigInt` vector to a new `BigInt` with a different number
        of bits and signedness.

        Parameters:
            bits: The number of bits for the new `BigInt`. Constraints: Must be
                a positive integer and a multiple of the bitwidth of word type.
            signed: A boolean indicating whether the new `BigInt` is signed
                (`True`) or unsigned (`False`).

        Returns:
            A new `BigInt` vector with the specified number of bits and
            signedness. The size and word type are preserved.
        """
        var result = BigInt[
            bits, size = Self.size, signed=signed, word_type = Self.word_type
        ](unsafe_uninitialized=True)

        @parameter
        if bits <= self.bits:

            @parameter
            for i in range(result.WORD_COUNT):
                result._storage[i] = self._storage[i]

        else:
            var extension = (self.is_negative() & signed).select(
                max_finite[word_type](), 0
            )

            @parameter
            for i in range(result.WORD_COUNT):

                @parameter
                if i < self.WORD_COUNT:
                    result._storage[i] = self._storage[i]
                else:
                    result._storage[i] = extension

        return result

    @always_inline
    fn cast[type: DType](self) -> SIMD[type, size]:
        """Casts the `BigInt` vector to a SIMD vector with the same size.

        Parameters:
            type: The type of the SIMD vector to cast to. Constraints: Must be
                an integral type.

        Returns:
            A SIMD vector containing the values from the `BigInt` vector, with
            the specified type.
        """
        constrained[type.is_integral(), "type must be an integral type"]()

        alias TYPE_BITWIDTH = type.bitwidth()
        alias MAX_COUNT = _conditional[
            TYPE_BITWIDTH > bits,
            Self.WORD_COUNT,
            TYPE_BITWIDTH // Self.WORD_TYPE_BITWIDTH,
        ]()

        var result = self._storage[0].cast[type]()

        @parameter
        if TYPE_BITWIDTH <= Self.WORD_TYPE_BITWIDTH:
            return result
        else:

            @parameter
            for i in range(1, MAX_COUNT):
                result += self._storage[i].cast[type]() << (
                    Self.WORD_TYPE_BITWIDTH * i
                )

            @parameter
            if signed and TYPE_BITWIDTH > bits:
                alias MASK = ~Scalar[type](0) << bits
                return self.is_negative().select(result | MASK, result)
            else:
                return result

    @always_inline
    fn clear_most_significant_bit(inout self):
        """Clears the most significant bit of the `BigInt` vector, element-wise.
        """
        self._storage[Self.WORD_COUNT - 1] &= _mask_trailing_ones[
            word_type, Self.WORD_TYPE_BITWIDTH - 1
        ]()

    @always_inline
    fn get_most_significant_bit(self) -> SIMD[DType.bool, size]:
        """Gets the most significant bit in each element of the `BigInt` vector.

        Returns:
            A SIMD vector containing the most significant bit for each element
            in the `BigInt` vector.
        """
        var msb = self._storage[Self.WORD_COUNT - 1] >> (
            Self.WORD_TYPE_BITWIDTH - 1
        )
        return msb.cast[DType.bool]()

    @always_inline
    fn set_most_significant_bit(inout self):
        """Sets the most significant bit of the `BigInt` vector, element-wise.
        """
        self._storage[Self.WORD_COUNT - 1] |= _mask_leading_ones[word_type, 1]()

    @always_inline
    fn count_leading_zeros(self) -> SIMD[word_type, size]:
        """Counts the number of leading zeros in each element of the `BigInt`
        vector.

        Returns:
            A SIMD vector containing the count of leading zeros for each element
            in the `BigInt` vector.
        """
        return _count_leading_zeros(self._storage)

    @always_inline
    fn is_negative(self) -> SIMD[DType.bool, size]:
        """Checks if the `BigInt` vector is negative, element-wise.

        Returns:
            A SIMD vector of booleans indicating whether each element in the
            `BigInt` vector is negative.
        """
        return self.get_most_significant_bit() & signed

    @always_inline
    fn is_zero(self) -> SIMD[DType.bool, size]:
        """Checks if the `BigInt` vector is zero, element-wise.

        Returns:
            A SIMD vector of booleans indicating whether each element in the
            `BigInt` vector is zero.
        """
        var result = SIMD[DType.bool, size](True)

        @parameter
        for i in range(Self.WORD_COUNT):
            result &= self._storage[i] == 0

            if not any(result):
                break

        return result


--- src/specials/utils/functional.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #

"""Implements higher-order functions."""

import math

from algorithm.functional import parallelize
from sys.info import num_performance_cores, simdwidthof


# ===----------------------------------------------------------------------=== #
# Elementwise
# ===----------------------------------------------------------------------=== #


fn elementwise[
    func: fn[Int] (Int, /) capturing -> None,
    *,
    simd_width: Int,
](num_elements: Int):
    """Executes `func[simd_width](index)`, possibly as subtasks, for each index
    in the range `[0, num_elements)`.

    Constraints:
        The SIMD width must be a positive integer value.

    Parameters:
        func: The function to apply at each index. It should not return a value.
        simd_width: The SIMD width to use.

    Arguments:
        num_elements: The number of elements to process.
    """
    elementwise[func, simd_width=simd_width](
        num_elements, min_simds_per_core=1024 // num_performance_cores()
    )


fn elementwise[
    func: fn[Int] (Int, /) capturing -> None,
    *,
    simd_width: Int,
](num_elements: Int, *, min_simds_per_core: Int):
    """Executes `func[simd_width](index)`, possibly as subtasks, for each index
    in the range `[0, num_elements)`.

    Constraints:
        The SIMD width must be a positive integer value.

    Parameters:
        func: The function to apply at each index. It should not return a value.
        simd_width: The SIMD width to use.

    Arguments:
        num_elements: The number of elements to process.
        min_simds_per_core: The minimum number of SIMD vectors per performance
            core to try to execute the `func` function in parallel.
    """
    constrained[simd_width > 0, "SIMD width must be a positive integer value"]()

    debug_assert(
        num_elements > 0, "number of elements must be a positive integer value"
    )
    debug_assert(
        min_simds_per_core > 0,
        "minimum SIMD vectors per core must be a positive integer value",
    )

    var num_simds = num_elements // simd_width
    var num_cores = num_performance_cores()
    var num_workers = 1

    if num_simds >= (num_cores * max(min_simds_per_core, 1)):
        num_workers = num_cores

    var num_tasks = num_workers
    var num_elements_per_tasks = (num_simds // num_tasks) * simd_width
    var num_remaining_elements = (
        num_elements - num_elements_per_tasks * num_tasks
    )

    @always_inline
    @parameter
    fn task_fn(task_id: Int):
        var start = task_id * num_elements_per_tasks
        var end: Int

        if task_id == (num_tasks - 1):
            end = num_simds * simd_width
        else:
            end = start + num_elements_per_tasks

        for index in range(start, end, simd_width):
            func[simd_width](index)

    if num_tasks > 1:
        parallelize[task_fn](num_tasks, num_workers)
    else:
        task_fn(0)

    if num_remaining_elements > 0:
        var start = num_elements - simd_width

        if start >= 0:
            func[simd_width](start)
        else:
            start = num_elements - num_remaining_elements
            for index in range(start, num_elements):
                func[1](index)


# ===----------------------------------------------------------------------=== #
# For Loop
# ===----------------------------------------------------------------------=== #


@always_inline
fn _fori_loop_impl[
    func: fn[Int, /] () capturing -> None,
    start: Int,
    end: Int,
    step: Int,
]():
    """Implements `fori_loop` using recursion."""

    @parameter
    if (step > 0 and start < end) or (step < 0 and start > end):
        func[start]()
        _fori_loop_impl[func, start + step, end, step]()


@always_inline
fn fori_loop[
    func: fn[Int, /] () capturing -> None,
    start: Int,
    end: Int,
    step: Int,
]():
    """Applies a for loop.

    Constraints:
        The step must be a non-zero integer value.

    Parameters:
        func: The function to apply at each iteration. The function should take
            a single Int parameter and not return a value.
        start: The loop index lower bound (inclusive).
        end: The loop index upper bound (exclusive).
        step: The loop index increment.
    """
    constrained[step != 0, "step must be a non-zero integer value"]()

    _fori_loop_impl[func, start, end, step]()


@always_inline
fn _fori_loop_impl[
    func: fn[Int, /] () capturing -> Bool,
    start: Int,
    end: Int,
    step: Int,
]():
    """Implements `fori_loop` with conditional execution using recursion."""

    @parameter
    if (step > 0 and start < end) or (step < 0 and start > end):
        if func[start]():
            _fori_loop_impl[func, start + step, end, step]()


@always_inline
fn fori_loop[
    func: fn[Int, /] () capturing -> Bool,
    start: Int,
    end: Int,
    step: Int,
]():
    """Applies a for loop with conditional execution.

    Constraints:
        The step must be a non-zero integer value.

    Parameters:
        func: The function to apply at each iteration. The function should take
            a single Int parameter and return a boolean value. The loop continues
            only if the result of `func` is `True`; otherwise, it terminates.
        start: The loop index lower bound (inclusive).
        end: The loop index upper bound (exclusive).
        step: The loop index increment.
    """
    constrained[step != 0, "step must be a non-zero integer value"]()

    _fori_loop_impl[func, start, end, step]()


--- src/specials/utils/numerics.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #

"""Implements utilities to work with numeric types."""

import math


@always_inline
fn _float_limits_construction_checks[type: DType]():
    """Checks if the type is valid."""
    constrained[
        type == DType.float16 or type == DType.float32 or type == DType.float64,
        (
            "type must be an IEEE 754-2008 floating-point: `DType.float16`,"
            " `DType.float32`, or `DType.float64`."
        ),
    ]()


@always_inline
fn _digits[type: DType]() -> IntLiteral:
    """
    Returns the number of `radix` digits that the given type can represent
    without loss of precision.
    """
    _float_limits_construction_checks[type]()

    @parameter
    if type == DType.float16:
        return 11
    elif type == DType.float32:
        return 24
    else:
        debug_assert(type == DType.float64, "type must be `DType.float64`")
        return 53


@always_inline
fn _max_exponent[type: DType]() -> IntLiteral:
    """
    Returns the maximum positive integer such that `radix` raised to
    `(max_exponent-1)` generates a representable finite floating-point number.
    """

    @parameter
    if type == DType.float16:
        return 16
    elif type == DType.float32:
        return 128
    else:
        debug_assert(type == DType.float64, "type must be `DType.float64`")
        return 1024


@always_inline
fn _min_exponent[type: DType]() -> IntLiteral:
    """
    Returns the minimum negative integer such that `radix` raised to
    `(min_exponent-1)` generates a normalized floating-point number.
    """

    @parameter
    if type == DType.float16:
        return -13
    elif type == DType.float32:
        return -125
    else:
        debug_assert(type == DType.float64, "type must be `DType.float64`")
        return -1021


@register_passable("trivial")
struct FloatLimits[type: DType]:
    """Machine limits for floating-point types.

    Constraints:
        The type must be an IEEE 754-2008 floating-point: `DType.float16`,
        `DType.float32`, or `DType.float64`.

    Parameters:
        type: The floating-point for which `FloatLimits` returns information.
    """

    alias digits: IntLiteral = _digits[type]()
    """
    The number of `radix` digits that the given type can represent without loss
    of precision.
    """

    alias max_exponent: IntLiteral = _max_exponent[type]()
    """
    Maximum positive integer such that `radix` raised to `(max_exponent-1)`
    generates a representable finite floating-point number.
    """

    alias min_exponent: IntLiteral = _min_exponent[type]()
    """
    Minimum negative integer such that `radix` raised to `(min_exponent-1)`
    generates a normalized floating-point number.
    """

    alias radix: IntLiteral = 2
    """The integral base used for the representation of the given type."""

    @staticmethod
    @always_inline
    fn denorm_min() -> Scalar[type]:
        """Returns the minimum positive denormalized value of the given type."""
        return math.ldexp(
            math.ldexp[type, 1](1.0, Self.min_exponent), -Self.digits
        )

    @staticmethod
    @always_inline
    fn epsilon() -> Scalar[type]:
        """
        Returns the difference between 1.0 and the next representable value of
        the given type.
        """
        return math.ldexp[type, 1](1.0, -Self.digits + 1)

    @staticmethod
    @always_inline
    fn epsilon_neg() -> Scalar[type]:
        """
        Returns the difference between 1.0 and the previous representable value
        of the given type.
        """
        return math.ldexp[type, 1](1.0, -Self.digits)

    @staticmethod
    @always_inline
    fn lowest() -> Scalar[type]:
        """Returns the most negative finite value of the given type."""
        return -Self.max()

    @staticmethod
    @always_inline
    fn max() -> Scalar[type]:
        """Returns the maximum finite value of the given type."""
        return math.ldexp[type, 1](
            2.0 * (1.0 - Self.epsilon_neg()), Self.max_exponent - 1
        )

    @staticmethod
    @always_inline
    fn min() -> Scalar[type]:
        """Returns the minimum positive normalized value of the given type."""
        return math.ldexp[type, 1](1.0, Self.min_exponent - 1)


--- test/_internal/test_math.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
# RUN: %mojo %build_dir %assertion_flag %debug_level %sanitize_checks %s

"""Tests for mathematical utilities."""

import math

from python import Python

from specials._internal.math import ldexp
from specials.utils.numerics import FloatLimits
from test_utils import UnitTest


fn _np_ldexp[
    type: DType
](x: Scalar[type], exp: Scalar[DType.int32]) raises -> Scalar[type]:
    var np = Python.import_module("numpy")
    var result = np.ldexp(x, exp)
    return result.to_float64().cast[type]()


fn test_ldexp_float_max[type: DType]() raises:
    var unit_test = UnitTest("test_ldexp_float_max_" + str(type))

    var x: Scalar[type] = 1.0 - FloatLimits[type].epsilon_neg()
    var exp: Scalar[DType.int32] = FloatLimits[type].max_exponent

    var expected = _np_ldexp[type](x, exp)
    var actual = ldexp[type](x, exp)

    unit_test.assert_equal(actual, expected)
    unit_test.assert_true(
        math.isfinite(actual).reduce_and(), msg="max should be finite"
    )


fn test_ldexp_float_smallest_normal[type: DType]() raises:
    var unit_test = UnitTest("test_ldexp_float_smallest_normal_" + str(type))

    var x: Scalar[type] = 1.0
    var exp: Scalar[DType.int32] = FloatLimits[type].min_exponent - 1

    var expected = _np_ldexp[type](x, exp)
    var actual = ldexp[type](x, exp)

    unit_test.assert_equal(actual, expected)
    unit_test.assert_true(
        (actual > 0.0).reduce_and(), msg="smallest_normal should be positive"
    )


fn test_ldexp_float_smallest_subnormal[type: DType]() raises:
    var unit_test = UnitTest("test_ldexp_float_smallest_subnormal_" + str(type))

    var x: Scalar[type] = 1.0
    var exp: Scalar[DType.int32] = FloatLimits[type].min_exponent - FloatLimits[
        type
    ].digits

    var expected = _np_ldexp[type](x, exp)
    var actual = ldexp[type](x, exp)

    unit_test.assert_equal(actual, expected)
    unit_test.assert_true(
        (actual > 0.0).reduce_and(), msg="smallest_subnormal should be positive"
    )


fn main() raises:
    test_ldexp_float_max[DType.float64]()
    test_ldexp_float_max[DType.float32]()

    test_ldexp_float_smallest_normal[DType.float64]()
    test_ldexp_float_smallest_normal[DType.float32]()

    test_ldexp_float_smallest_subnormal[DType.float64]()
    test_ldexp_float_smallest_subnormal[DType.float32]()


--- test/_internal/test_polynomial.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2023 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
# RUN: %mojo %build_dir %assertion_flag %debug_level %sanitize_checks %s

"""Tests for polynomial utilities."""

from sys.info import simdwidthof

from specials._internal import polynomial as P
from test_utils import UnitTest


fn test_chebyshev[type: DType]() raises:
    var unit_test = UnitTest("test_chebyshev_" + str(type))

    alias simd_width = simdwidthof[type]()
    var p = P.Chebyshev[4, type, simd_width].from_coefficients[
        4.0, 3.0, 2.0, 1.0
    ]()

    unit_test.assert_equal(len(p), 4)
    unit_test.assert_equal(p.degree(), 3)
    unit_test.assert_equal(p.get[0](), SIMD[type, simd_width](4.0))
    unit_test.assert_equal(p.get[1](), 3.0)
    unit_test.assert_equal(p.get[2](), 2.0)
    unit_test.assert_equal(p.get[3](), 1.0)


fn test_chebyshev_small[type: DType]() raises:
    var unit_test = UnitTest("test_chebyshev_small_" + str(type))

    alias simd_width = simdwidthof[type]()
    var p = P.Chebyshev[1, type, simd_width].from_coefficients[1.0]()

    unit_test.assert_equal(len(p), 1)
    unit_test.assert_equal(p.degree(), 0)
    unit_test.assert_equal(p.get[0](), SIMD[type, simd_width](1.0))


fn test_chebyshev_hexadecimal[type: DType]() raises:
    var unit_test = UnitTest("test_chebyshev_hexadecimal_" + str(type))

    alias simd_width = simdwidthof[type]()
    var p: P.Chebyshev[2, type, simd_width]

    @parameter
    if type == DType.float32:
        p = P.Chebyshev[2, type, simd_width].from_hexadecimal_coefficients[
            0x3F80_0000,
            0x3F00_0000,
        ]()
    else:
        p = P.Chebyshev[2, type, simd_width].from_hexadecimal_coefficients[
            0x3FF00000_00000000,
            0x3FE00000_00000000,
        ]()

    unit_test.assert_equal(len(p), 2)
    unit_test.assert_equal(p.degree(), 1)
    unit_test.assert_equal(p.get[0](), SIMD[type, simd_width](1.0))
    unit_test.assert_equal(p.get[1](), 0.5)


fn test_chebyshev_truncate[type: DType]() raises:
    var unit_test = UnitTest("test_chebyshev_truncate_" + str(type))

    alias simd_width = simdwidthof[type]()
    var p = P.Chebyshev[4, type, simd_width].from_coefficients[
        4.0, 3.0, 2.0, 1.0
    ]()

    var q = p.truncate[1]()

    unit_test.assert_equal(len(q), 1)
    unit_test.assert_equal(q.degree(), 0)
    unit_test.assert_equal(q.get[0](), SIMD[type, simd_width](4.0))

    var r = p.truncate[4]()

    unit_test.assert_equal(len(r), 4)
    unit_test.assert_equal(r.degree(), 3)
    unit_test.assert_equal(r.get[0](), SIMD[type, simd_width](4.0))
    unit_test.assert_equal(r.get[1](), 3.0)
    unit_test.assert_equal(r.get[2](), 2.0)
    unit_test.assert_equal(r.get[3](), 1.0)


fn test_chebyshev_economize[type: DType]() raises:
    var unit_test = UnitTest("test_chebyshev_economize_" + str(type))

    alias simd_width = simdwidthof[type]()
    alias p = P.Chebyshev[4, type, simd_width].from_coefficients[
        4.0, 2.0, 1.0, 0.5
    ]()
    alias num_terms = p.economize[1.5]()

    unit_test.assert_equal(num_terms, 2)

    var q = p.truncate[num_terms]()

    unit_test.assert_equal(len(q), 2)
    unit_test.assert_equal(q.degree(), 1)
    unit_test.assert_equal(q.get[0](), SIMD[type, simd_width](4.0))
    unit_test.assert_equal(q.get[1](), 2.0)


fn test_chebyshev_evaluate[type: DType]() raises:
    var unit_test = UnitTest("test_chebyshev_evaluate_" + str(type))

    alias simd_width = 4
    var p = P.Chebyshev[4, type, simd_width].from_coefficients[
        4.0, 3.0, 2.0, 1.0
    ]()
    var x = SIMD[type, simd_width](-1.0, -0.5, 0.5, 1.0)

    # Expected values computed using `numpy.polynomial.Chebyshev`.
    var expected = SIMD[type, simd_width](2.0, 2.5, 3.5, 10.0)
    var actual = p(x)

    unit_test.assert_all_close(actual, expected, atol=0.0, rtol=1e-12)


fn test_polynomial[type: DType]() raises:
    var unit_test = UnitTest("test_polynomial_" + str(type))

    alias simd_width = simdwidthof[type]()
    var p = P.Polynomial[4, type, simd_width].from_coefficients[
        4.0, 3.0, 2.0, 1.0
    ]()

    unit_test.assert_equal(len(p), 4)
    unit_test.assert_equal(p.degree(), 3)
    unit_test.assert_equal(p.get[0](), SIMD[type, simd_width](4.0))
    unit_test.assert_equal(p.get[1](), 3.0)
    unit_test.assert_equal(p.get[2](), 2.0)
    unit_test.assert_equal(p.get[3](), 1.0)


fn test_polynomial_small[type: DType]() raises:
    var unit_test = UnitTest("test_polynomial_small_" + str(type))

    alias simd_width = simdwidthof[type]()
    var p = P.Polynomial[1, type, simd_width].from_coefficients[1.0]()

    unit_test.assert_equal(len(p), 1)
    unit_test.assert_equal(p.degree(), 0)
    unit_test.assert_equal(p.get[0](), SIMD[type, simd_width](1.0))


fn test_polynomial_hexadecimal[type: DType]() raises:
    var unit_test = UnitTest("test_polynomial_hexadecimal" + str(type))

    alias simd_width = simdwidthof[type]()
    var p: P.Polynomial[2, type, simd_width]

    @parameter
    if type == DType.float32:
        p = P.Polynomial[2, type, simd_width].from_hexadecimal_coefficients[
            0x3F80_0000,
            0x3F00_0000,
        ]()
    else:
        p = P.Polynomial[2, type, simd_width].from_hexadecimal_coefficients[
            0x3FF00000_00000000,
            0x3FE00000_00000000,
        ]()

    unit_test.assert_equal(len(p), 2)
    unit_test.assert_equal(p.degree(), 1)
    unit_test.assert_equal(p.get[0](), SIMD[type, simd_width](1.0))
    unit_test.assert_equal(p.get[1](), 0.5)


fn test_polynomial_truncate[type: DType]() raises:
    var unit_test = UnitTest("test_polynomial_truncate_" + str(type))

    alias simd_width = simdwidthof[type]()
    var p = P.Polynomial[4, type, simd_width].from_coefficients[
        4.0, 3.0, 2.0, 1.0
    ]()

    var q = p.truncate[1]()

    unit_test.assert_equal(len(q), 1)
    unit_test.assert_equal(q.degree(), 0)
    unit_test.assert_equal(q.get[0](), SIMD[type, simd_width](4.0))

    var r = p.truncate[4]()

    unit_test.assert_equal(len(r), 4)
    unit_test.assert_equal(r.degree(), 3)
    unit_test.assert_equal(r.get[0](), SIMD[type, simd_width](4.0))
    unit_test.assert_equal(r.get[1](), 3.0)
    unit_test.assert_equal(r.get[2](), 2.0)
    unit_test.assert_equal(r.get[3](), 1.0)


fn test_polynomial_evaluate[type: DType]() raises:
    var unit_test = UnitTest("test_polynomial_evaluate_" + str(type))

    alias simd_width = 4
    var p = P.Polynomial[4, type, simd_width].from_coefficients[
        4.0, 3.0, 2.0, 1.0
    ]()
    var x = SIMD[type, simd_width](-10.0, -2.5, 0.0, 1.0)

    # Expected values computed using `numpy.polynomial.Polynomial`.
    var expected = SIMD[type, simd_width](-826.0, -6.625, 4.0, 10.0)
    var actual = p(x)

    unit_test.assert_all_close(actual, expected, atol=0.0, rtol=1e-12)


fn main() raises:
    # Chebyshev Series
    test_chebyshev[DType.float32]()
    test_chebyshev[DType.float64]()

    test_chebyshev_small[DType.float32]()
    test_chebyshev_small[DType.float64]()

    test_chebyshev_hexadecimal[DType.float32]()
    test_chebyshev_hexadecimal[DType.float64]()

    test_chebyshev_truncate[DType.float32]()
    test_chebyshev_truncate[DType.float64]()

    test_chebyshev_economize[DType.float32]()
    test_chebyshev_economize[DType.float64]()

    test_chebyshev_evaluate[DType.float32]()
    test_chebyshev_evaluate[DType.float64]()

    # Power Series
    test_polynomial[DType.float32]()
    test_polynomial[DType.float64]()

    test_polynomial_small[DType.float32]()
    test_polynomial_small[DType.float64]()

    test_polynomial_hexadecimal[DType.float32]()
    test_polynomial_hexadecimal[DType.float64]()

    test_polynomial_truncate[DType.float32]()
    test_polynomial_truncate[DType.float64]()

    test_polynomial_evaluate[DType.float32]()
    test_polynomial_evaluate[DType.float64]()


--- test/_internal/test_table.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2023 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
# RUN: %mojo %build_dir %assertion_flag %debug_level %sanitize_checks %s

"""Tests for table utilities."""

import math

from specials._internal.table import FloatTable
from test_utils import UnitTest


fn test_sized() raises:
    var unit_test = UnitTest("test_sized")

    var table = FloatTable[4, DType.float32].from_values[1.0, 2.0, 3.0, 4.0]()
    var expected = 4
    var actual = len(table)

    unit_test.assert_equal(actual, expected)


fn test_unsafe_lookup[type: DType]() raises:
    var unit_test = UnitTest("test_unsafe_lookup_" + str(type))

    var table = FloatTable[4, type].from_values[1.0, 2.0, 3.0, 4.0]()
    var index = SIMD[DType.int32, 4](3, 0, 2, 1)
    var expected = SIMD[type, 4](4.0, 1.0, 3.0, 2.0)
    var actual = table.unsafe_lookup(index)

    unit_test.assert_equal(actual, expected)


fn test_lookup[type: DType]() raises:
    var unit_test = UnitTest("test_lookup_" + str(type))

    var table = FloatTable[4, type].from_values[1.0, 2.0, 3.0, 4.0]()
    var index = SIMD[DType.int32, 4](3, 0, 2, 1)
    var expected = SIMD[type, 4](4.0, 1.0, 3.0, 2.0)
    var actual = table.lookup(index)

    unit_test.assert_equal(actual, expected)


fn test_out_of_bound[type: DType]() raises:
    var unit_test = UnitTest("test_lookup_out_of_bound_" + str(type))

    var table = FloatTable[4, type].from_values[1.0, 2.0, 3.0, 4.0]()
    var index = SIMD[DType.int32, 4](4, 0, 2, 1)
    var expected = SIMD[type, 4](math.nan[type](), 1.0, 3.0, 2.0)
    var actual = table.lookup(index)

    unit_test.assert_equal(actual, expected)


fn test_hexadecimal_values[type: DType]() raises:
    var unit_test = UnitTest("test_hexadecimal_values_" + str(type))

    var table: FloatTable[2, type]

    @parameter
    if type == DType.float32:
        table = FloatTable[2, type].from_hexadecimal_values[
            0x3F80_0000,
            0x3F00_0000,
        ]()
    else:
        table = FloatTable[2, type].from_hexadecimal_values[
            0x3FF00000_00000000,
            0x3FE00000_00000000,
        ]()

    var index = SIMD[DType.int32, 2](0, 1)
    var expected = SIMD[type, 2](1.0, 0.5)
    var actual = table.lookup(index)

    unit_test.assert_equal(actual, expected)


fn main() raises:
    test_sized()

    test_unsafe_lookup[DType.float32]()
    test_unsafe_lookup[DType.float64]()

    test_lookup[DType.float32]()
    test_lookup[DType.float64]()

    test_out_of_bound[DType.float32]()
    test_out_of_bound[DType.float64]()

    test_hexadecimal_values[DType.float32]()
    test_hexadecimal_values[DType.float64]()


--- test/elementary/test_common_constants.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
# RUN: %mojo %build_dir %assertion_flag %debug_level %sanitize_checks %s

"""Tests for common constants."""

from python import Python

from specials.elementary.common_constants import ExpTable, LogConstants
from test_utils import UnitTest


fn _np_exp2[type: DType](x: Scalar[type]) raises -> Scalar[type]:
    var np = Python.import_module("numpy")
    var result = np.exp2(x)
    return result.to_float64().cast[type]()


fn test_exp_table[type: DType]() raises:
    var unit_test = UnitTest("test_exp_table_" + str(type))

    var expected_table_size = 32

    unit_test.assert_equal(len(ExpTable[type].lead), expected_table_size)
    unit_test.assert_equal(len(ExpTable[type].trail), expected_table_size)

    var rtol: Scalar[type]

    @parameter
    if type == DType.float32:
        rtol = 1e-6
    else:  # type == DType.float64
        rtol = 1e-15

    for i in range(expected_table_size):
        var index = Scalar[DType.int32](i)
        var x_lead = ExpTable[type].lead.lookup(index)
        var x_trail = ExpTable[type].trail.lookup(index)
        var expected = _np_exp2[type](Scalar[type](i) / expected_table_size)
        var actual = x_lead + x_trail

        unit_test.assert_all_close(actual, expected, atol=0.0, rtol=rtol)


fn test_log_constants[type: DType]() raises:
    var unit_test = UnitTest("test_log_constants_" + str(type))

    var expected_table_size = 129

    unit_test.assert_equal(
        len(LogConstants[type].inv_fraction1), expected_table_size
    )
    unit_test.assert_equal(
        len(LogConstants[type].log_fraction1_lead), expected_table_size
    )
    unit_test.assert_equal(
        len(LogConstants[type].log_fraction1_trail), expected_table_size
    )

    fn _np_inv_fraction1[type: DType](j: Int) raises -> Scalar[type]:
        var np = Python.import_module("numpy")
        var result = np.reciprocal(1.0 + np.ldexp(j, -7))
        return result.to_float64().cast[type]()

    fn _np_log_fraction1[type: DType](j: Int) raises -> Scalar[type]:
        var np = Python.import_module("numpy")
        var result = np.log1p(np.ldexp(j, -7))
        return result.to_float64().cast[type]()

    var rtol: Scalar[type]

    @parameter
    if type == DType.float32:
        rtol = 1e-6
    else:  # type == DType.float64
        rtol = 1e-15

    for j in range(expected_table_size):
        var index = Scalar[DType.int32](j)

        # inv_fraction1
        var inv_fraction1 = LogConstants[type].inv_fraction1.lookup(index)
        var expected = _np_inv_fraction1[type](j)
        var actual = inv_fraction1

        unit_test.assert_all_close(actual, expected, atol=0.0, rtol=rtol)

        # log_fraction1
        var log_fraction1_lead = LogConstants[type].log_fraction1_lead.lookup(
            index
        )
        var log_fraction1_trail = LogConstants[type].log_fraction1_trail.lookup(
            index
        )
        expected = _np_log_fraction1[type](j)
        actual = log_fraction1_lead + log_fraction1_trail

        unit_test.assert_all_close(actual, expected, atol=0.0, rtol=rtol)


fn main() raises:
    test_exp_table[DType.float64]()
    test_exp_table[DType.float32]()

    test_log_constants[DType.float64]()
    test_log_constants[DType.float32]()


--- test/elementary/test_exp.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2023 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
# RUN: %mojo %build_dir %assertion_flag %debug_level %sanitize_checks %s

"""Tests for the `exp` function."""

import math

from python import Python
from utils.static_tuple import StaticTuple

from specials.elementary.exp import exp
from specials.utils.numerics import FloatLimits
from test_utils import UnitTest


fn _mp_exp[type: DType](x: Scalar[type]) raises -> Scalar[type]:
    var mp = Python.import_module("mpmath")
    var result = mp.exp(mp.mpf(x))
    return result.to_float64().cast[type]()


fn test_exp[type: DType]() raises:
    var unit_test = UnitTest("test_exp_" + str(type))

    var xs = StaticTuple[Scalar[type], 9](
        -10.0, -1.0, -0.1, -0.01, 0.0, 0.01, 0.1, 1.0, 10.0
    )

    var rtol: Scalar[type]

    @parameter
    if type == DType.float32:
        rtol = 1e-6
    else:  # type == DType.float64
        rtol = 1e-15

    for i in range(len(xs)):
        var x = xs[i]
        var expected = _mp_exp[type](x)
        var actual = exp(x)

        unit_test.assert_all_close(actual, expected, atol=0.0, rtol=rtol)


fn test_exp_special_cases[type: DType]() raises:
    var unit_test = UnitTest("test_exp_special_cases_" + str(type))

    var log2 = 0.6931471805599453
    var xmin = Scalar[type](FloatLimits[type].min_exponent - 1) * log2
    var xeps = 0.5 * FloatLimits[type].epsilon_neg()
    var xmax = math.nextafter(
        Scalar[type](FloatLimits[type].max_exponent) * log2, 0.0
    )
    var nan = math.nan[type]()
    var inf = math.inf[type]()

    var xs = StaticTuple[Scalar[type], 13](
        nan,
        -inf,
        xmin - 1.0,
        xmin,
        xmin + 1.0,
        -xeps,
        -0.1 * xeps,
        0.0,
        0.1 * xeps,
        xmax - 1.0,
        xmax,
        xmax + 1.0,
        inf,
    )

    var rtol: Scalar[type]

    @parameter
    if type == DType.float32:
        rtol = 1e-6
    else:  # type == DType.float64
        rtol = 1e-15

    for i in range(len(xs)):
        var x = xs[i]
        var actual = exp(x)
        var expected: Scalar[type]

        if math.isnan(x):
            expected = nan
        elif x > xmax:
            expected = inf
        else:
            expected = _mp_exp[type](x)

        unit_test.assert_all_close(actual, expected, atol=0.0, rtol=rtol)


fn main() raises:
    # Setting the mpmath precision for this module
    var mp = Python.import_module("mpmath")
    mp.mp.dps = 50

    test_exp[DType.float64]()
    test_exp[DType.float32]()

    test_exp_special_cases[DType.float64]()
    test_exp_special_cases[DType.float32]()


--- test/elementary/test_exp2.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
# RUN: %mojo %build_dir %assertion_flag %debug_level %sanitize_checks %s

"""Tests for the `exp2` function."""

import math

from python import Python
from utils.static_tuple import StaticTuple

from specials.elementary.exp2 import exp2
from specials.utils.numerics import FloatLimits
from test_utils import UnitTest


fn _mp_exp2[type: DType](x: Scalar[type]) raises -> Scalar[type]:
    var mp = Python.import_module("mpmath")
    var result = mp.power(mp.mpf(2), mp.mpf(x))
    return result.to_float64().cast[type]()


fn test_exp2[type: DType]() raises:
    var unit_test = UnitTest("test_exp2_" + str(type))

    var xs = StaticTuple[Scalar[type], 9](
        -10.0, -1.0, -0.1, -0.01, 0.0, 0.01, 0.1, 1.0, 10.0
    )

    var rtol: Scalar[type]

    @parameter
    if type == DType.float32:
        rtol = 1e-6
    else:  # type == DType.float64
        rtol = 1e-15

    for i in range(len(xs)):
        var x = xs[i]
        var expected = _mp_exp2[type](x)
        var actual = exp2(x)

        unit_test.assert_all_close(actual, expected, atol=0.0, rtol=rtol)


fn test_exp2_special_cases[type: DType]() raises:
    var unit_test = UnitTest("test_exp2_special_cases_" + str(type))

    var xmin = Scalar[type](FloatLimits[type].min_exponent - 1)
    var xeps = 0.5 * FloatLimits[type].epsilon_neg()
    var xmax = math.nextafter(Scalar[type](FloatLimits[type].max_exponent), 0.0)
    var nan = math.nan[type]()
    var inf = math.inf[type]()

    var xs = StaticTuple[Scalar[type], 13](
        nan,
        -inf,
        xmin - 1.0,
        xmin,
        xmin + 1.0,
        -xeps,
        -0.1 * xeps,
        0.0,
        0.1 * xeps,
        xmax - 1.0,
        xmax,
        xmax + 1.0,
        inf,
    )

    var rtol: Scalar[type]

    @parameter
    if type == DType.float32:
        rtol = 1e-6
    else:  # type == DType.float64
        rtol = 1e-15

    for i in range(len(xs)):
        var x = xs[i]
        var actual = exp2(x)
        var expected: Scalar[type]

        if math.isnan(x):
            expected = nan
        elif x > xmax:
            expected = inf
        else:
            expected = _mp_exp2[type](x)

        unit_test.assert_all_close(actual, expected, atol=0.0, rtol=rtol)


fn main() raises:
    # Setting the mpmath precision for this module
    var mp = Python.import_module("mpmath")
    mp.mp.dps = 100

    test_exp2[DType.float64]()
    test_exp2[DType.float32]()

    test_exp2_special_cases[DType.float64]()
    test_exp2_special_cases[DType.float32]()


--- test/elementary/test_expm1.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
# RUN: %mojo %build_dir %assertion_flag %debug_level %sanitize_checks %s

"""Tests for the `expm1` function."""

import math

from memory.unsafe import bitcast

from python import Python
from utils.static_tuple import StaticTuple

from specials.elementary.expm1 import expm1
from specials.elementary.log import log
from specials.utils.numerics import FloatLimits
from test_utils import UnitTest


fn _mp_expm1[type: DType](x: Scalar[type]) raises -> Scalar[type]:
    var mp = Python.import_module("mpmath")
    var result = mp.expm1(mp.mpf(x))
    return result.to_float64().cast[type]()


fn test_expm1[type: DType]() raises:
    var unit_test = UnitTest("test_expm1_" + str(type))

    var xeps = FloatLimits[type].epsilon()
    var xs = StaticTuple[Scalar[type], 5](0.1 * xeps, 0.01, 0.1, 1.0, 10.0)

    var rtol: Scalar[type]

    @parameter
    if type == DType.float32:
        rtol = 1e-6
    else:  # type == DType.float64
        rtol = 1e-15

    for i in range(len(xs)):
        var x = xs[i]
        var expected = _mp_expm1[type](x)
        var actual = expm1(x)

        unit_test.assert_all_close(actual, expected, atol=0.0, rtol=rtol)


fn test_expm1_special_cases[type: DType]() raises:
    var unit_test = UnitTest("test_expm1_special_cases_" + str(type))

    var xeps: Scalar[type]
    var xsml_inf: Scalar[type]
    var xsml_sup: Scalar[type]
    var xmin: Scalar[type]
    var xmax: Scalar[type]
    var nan = math.nan[type]()
    var inf = math.inf[type]()

    @parameter
    if type == DType.float32:
        xeps = bitcast[type, DType.uint32](0x3300_0000)
        xsml_inf = bitcast[type, DType.uint32](0xBE93_4B11)
        xsml_sup = bitcast[type, DType.uint32](0x3E64_7FBF)
        xmin = bitcast[type, DType.uint32](0xC18A_A122)
        xmax = math.nextafter(log(FloatLimits[type].max()), 0.0)
    else:  # type == DType.float64
        xeps = bitcast[type, DType.uint64](0x3C900000_00000000)
        xsml_inf = bitcast[type, DType.uint64](0xBFD26962_1134DB93)
        xsml_sup = bitcast[type, DType.uint64](0x3FCC8FF7_C79A9A22)
        xmin = bitcast[type, DType.uint64](0xC042B708_872320E1)
        xmax = log(FloatLimits[type].max())

    var xs = StaticTuple[Scalar[type], 12](
        nan,
        -inf,
        xmin - 1.0,
        xmin,
        xsml_inf,
        -xeps,
        0.0,
        xeps,
        xsml_sup,
        xmax,
        xmax + 1.0,
        inf,
    )

    var rtol: Scalar[type]

    @parameter
    if type == DType.float32:
        rtol = 1e-6
    else:  # type == DType.float64
        rtol = 1e-15

    for i in range(len(xs)):
        var x = xs[i]
        var actual = expm1(x)
        var expected: Scalar[type]

        if math.isnan(x):
            expected = nan
        elif x < xmin:
            expected = -1.0
        elif x > xmax:
            expected = inf
        else:
            expected = _mp_expm1[type](x)

        unit_test.assert_all_close(actual, expected, atol=0.0, rtol=rtol)


fn main() raises:
    # Setting the mpmath precision for this module
    var mp = Python.import_module("mpmath")
    mp.mp.dps = 50

    test_expm1[DType.float64]()
    test_expm1[DType.float32]()

    test_expm1_special_cases[DType.float64]()
    test_expm1_special_cases[DType.float32]()


--- test/elementary/test_log.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
# RUN: %mojo %build_dir %assertion_flag %debug_level %sanitize_checks %s

"""Tests for the logarithm function."""

import math

from python import Python
from utils.static_tuple import StaticTuple

from specials.elementary.log import log
from specials.utils.numerics import FloatLimits
from test_utils import UnitTest


fn _mp_log[type: DType](x: Scalar[type]) raises -> Scalar[type]:
    var mp = Python.import_module("mpmath")
    var result = mp.log(mp.mpf(x))
    return result.to_float64().cast[type]()


fn test_log[type: DType]() raises:
    var unit_test = UnitTest("test_log_" + str(type))

    var xs = StaticTuple[Scalar[type], 8](
        1e-16,
        1e-4,
        0.1,
        0.99,
        5.0,
        10.0,
        1e4,
        1e16,
    )

    var rtol: Scalar[type]

    @parameter
    if type == DType.float32:
        rtol = 1e-6
    else:  # type == DType.float64
        rtol = 1e-15

    for i in range(len(xs)):
        var x = xs[i]
        var expected = _mp_log[type](x)
        var actual = log(x)

        unit_test.assert_all_close(actual, expected, atol=0.0, rtol=rtol)


fn test_log_special_cases[type: DType]() raises:
    var unit_test = UnitTest("test_log_special_cases_" + str(type))

    var xmin = FloatLimits[type].min()
    var xmax = FloatLimits[type].max()
    var nan = math.nan[type]()
    var inf = math.inf[type]()

    var xs = StaticTuple[Scalar[type], 13](
        nan,
        -inf,
        -1.0,
        0.0,
        0.5 * xmin,
        xmin,
        2.0 * xmin,
        1.0 - FloatLimits[type].epsilon_neg(),
        1.0,
        1.0 + FloatLimits[type].epsilon(),
        xmax - 1.0,
        xmax,
        inf,
    )

    var rtol: Scalar[type]

    @parameter
    if type == DType.float32:
        rtol = 1e-6
    else:  # type == DType.float64
        rtol = 1e-15

    for i in range(len(xs)):
        var x = xs[i]
        var actual = log(x)
        var expected: Scalar[type]

        if math.isnan(x) | (x < 0.0):
            expected = nan
        elif x == 0.0:
            expected = -inf
        elif x > xmax:
            expected = inf
        else:
            expected = _mp_log[type](x)

        unit_test.assert_all_close(actual, expected, atol=0.0, rtol=rtol)


fn main() raises:
    # Setting the mpmath precision for this module
    var mp = Python.import_module("mpmath")
    mp.mp.dps = 100

    test_log[DType.float64]()
    test_log[DType.float32]()

    test_log_special_cases[DType.float64]()
    test_log_special_cases[DType.float32]()


--- test/elementary/test_log1p.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
# RUN: %mojo %build_dir %assertion_flag %debug_level %sanitize_checks %s

"""Tests for the `log1p` function."""

import math

from python import Python
from utils.static_tuple import StaticTuple

from specials.elementary.log1p import log1p
from specials.utils.numerics import FloatLimits
from test_utils import UnitTest


fn _mp_log1p[type: DType](x: Scalar[type]) raises -> Scalar[type]:
    var mp = Python.import_module("mpmath")
    var result = mp.log1p(mp.mpf(x))
    return result.to_float64().cast[type]()


fn test_log1p[type: DType]() raises:
    var unit_test = UnitTest("test_log1p_" + str(type))

    var xs = StaticTuple[Scalar[type], 15](
        -0.9,
        -0.5,
        -0.1,
        -1e-4,
        -1e-8,
        -1e-16,
        0.0,
        1e-16,
        1e-8,
        1e-4,
        0.1,
        0.5,
        0.9,
        1.0,
        10.0,
    )

    var rtol: Scalar[type]

    @parameter
    if type == DType.float32:
        rtol = 1e-6
    else:  # type == DType.float64
        rtol = 1e-15

    for i in range(len(xs)):
        var x = xs[i]
        var expected = _mp_log1p[type](x)
        var actual = log1p(x)

        unit_test.assert_all_close(actual, expected, atol=0.0, rtol=rtol)


fn test_log1p_special_cases[type: DType]() raises:
    var unit_test = UnitTest("test_log1p_special_cases_" + str(type))

    var xmin = FloatLimits[type].min()
    var xeps = FloatLimits[type].epsilon_neg()
    var xlrg = math.ldexp(Scalar[type](1), FloatLimits[type].digits + 2)
    var xmax = FloatLimits[type].max()
    var nan = math.nan[type]()
    var inf = math.inf[type]()

    var xs = StaticTuple[Scalar[type], 13](
        nan,
        -2.0,
        -1.0,
        -1.0 + xeps,
        -xmin,
        -0.5 * xmin,
        0.5 * xmin,
        xmin,
        1.0 - xeps,
        xlrg - 1.0,
        xlrg + 1.0,
        xmax,
        inf,
    )

    var rtol: Scalar[type]

    @parameter
    if type == DType.float32:
        rtol = 1e-6
    else:  # type == DType.float64
        rtol = 1e-15

    for i in range(len(xs)):
        var x = xs[i]
        var actual = log1p(x)
        var expected: Scalar[type]

        if math.isnan(x) | (x < -1.0):
            expected = nan
        elif x == -1.0:
            expected = -inf
        elif x > xmax:
            expected = inf
        else:
            expected = _mp_log1p[type](x)

        unit_test.assert_all_close(actual, expected, atol=0.0, rtol=rtol)


fn main() raises:
    # Setting the mpmath precision for this module
    var mp = Python.import_module("mpmath")
    mp.mp.dps = 100

    test_log1p[DType.float64]()
    test_log1p[DType.float32]()

    test_log1p_special_cases[DType.float64]()
    test_log1p_special_cases[DType.float32]()


--- test/lit.cfg.py ---

# type: ignore
# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #

"""Configuration for the lit test runner used by the Specials test suite."""

import os
import platform
import shutil

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 = "Specials"

# suffixes: A list of file extensions to treat as test files
config.suffixes = [".mojo", ".py"]

# excludes: A list of files and directories ignored by the lit
# test runner when scanning for tests
# `test_utils` does not contain tests, just source code that we
# run `mojo package` on to be used by other tests
config.excludes = ["lit.cfg.py", "setup.py", "test_utils"]

# test_source_root: The root path where tests are located
test_dir = Path(__file__).parent.resolve()
config.test_source_root = test_dir

# The `run-tests.sh` script creates the build directory for us
build_dir = Path(__file__).parent.parent / "build"

# The tests are executed inside this build directory to avoid
# polluting the source tree
config.test_exec_root = build_dir / "test_output"

# This makes the OS name available for `REQUIRE` directives,
# e.g., `# REQUIRE: darwin`
config.available_features.add(platform.system().lower())


# Check if the `not` binary from LLVM is available
def has_not():
    return shutil.which("not") is not None


if has_not():
    config.available_features.add("has_not")

build_mode = os.getenv("BUILD_MODE", "debug")

if build_mode == "debug":
    assertion_flag = "-D MOJO_ENABLE_ASSERTIONS"
    debug_level = "--debug-level full"
else:
    assertion_flag = ""
    debug_level = "--debug-level none"

config.substitutions.insert(0, ("%mojo", "mojo run"))
config.substitutions.insert(1, ("%build_dir", f"-I {str(build_dir)}"))
config.substitutions.insert(2, ("%assertion_flag", assertion_flag))
config.substitutions.insert(3, ("%debug_level", "--debug-level full"))
config.substitutions.insert(4, ("%sanitize_checks", ""))
config.substitutions.insert(5, ("%pytest", "pytest"))

# 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",
        "PATH",
    ]
)


--- test/setup.py ---

# type: ignore
# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #

"""Setup configuration for the `test_utils` package."""

from setuptools import setup, find_packages


setup(
    name="test_utils",
    version="0.1.0",
    package_dir={"": "."},
    packages=find_packages(where="."),
)


--- test/test_gamma.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2023 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
# RUN: %mojo %build_dir %debug_level %sanitize_checks %s

"""Tests for gamma-related functions."""

import math

from python import Python
from utils.static_tuple import StaticTuple

import specials

from specials.utils.numerics import FloatLimits
from test_utils import UnitTest

# TODO: Add tests with `DType.float32` type for `lbeta`.
# TODO: Investigate why defining `MOJO_ENABLE_ASSERTIONS` leads to test failures.


fn test_lgamma_correction[type: DType]() raises:
    var unit_test = UnitTest("test_lgamma_correction_" + str(type))

    var x = SIMD[type, 4](10.0, 100.0, 1000.0, 10000.0)

    # The expected values were computed using `mpmath`.
    var expected = SIMD[type, 4](
        8.330563433362871256469318659629e-3,
        8.333305556349146833812416928147e-4,
        8.333333055555634920575396909572e-5,
        8.333333330555555563492063432540e-6,
    )
    var actual = specials.lgamma_correction(x)

    var rtol: Scalar[type]

    @parameter
    if type == DType.float32:
        rtol = 1e-6
    else:  # type == DType.float64
        rtol = 1e-12

    unit_test.assert_all_close(actual, expected, atol=0.0, rtol=rtol)


fn test_lgamma_correction_special_cases[type: DType]() raises:
    var unit_test = UnitTest(
        "test_lgamma_correction_special_cases_" + str(type)
    )

    var inf = math.inf[type]()
    var nan = math.nan[type]()
    var zero = Scalar[type](0.0)
    var x = SIMD[type, 4](nan, -inf, zero, inf)

    var expected = SIMD[type, 4](nan, nan, nan, zero)
    var actual = specials.lgamma_correction(x)

    unit_test.assert_equal(actual, expected)


fn test_lgamma1p_region1[type: DType]() raises:
    var unit_test = UnitTest("test_lgamma1p_region1_" + str(type))

    alias eps_f32 = FloatLimits[DType.float32].epsilon().cast[type]()
    var x = SIMD[type, 4](-0.2, 0.0 - eps_f32, 0.0 + eps_f32, 0.60 - eps_f32)

    # The expected values were computed using `mpmath`.
    var expected = SIMD[type, 4](
        1.520596783998375994920398994673e-1,
        6.880948101845375157827140831409e-8,
        -6.88094576425347115116354810460e-8,
        -1.12591780722776981673572240680e-1,
    )

    var rtol: Scalar[type]

    @parameter
    if type == DType.float32:
        rtol = 1e-6
    else:  # type == DType.float64
        rtol = 1e-12

    unit_test.assert_all_close(
        specials.lgamma1p(x), expected, atol=0.0, rtol=rtol
    )


fn test_lgamma1p_region2[type: DType]() raises:
    var unit_test = UnitTest("test_lgamma1p_region2_" + str(type))

    alias eps_f32 = FloatLimits[DType.float32].epsilon().cast[type]()
    var x = SIMD[type, 4](0.6, 1.0 - eps_f32, 1.0 + eps_f32, 1.25)

    # The expected values were computed using `mpmath`.
    var expected = SIMD[type, 4](
        -1.12591765696755786387475561192e-1,
        -5.03998156377554207114124403668e-8,
        5.039982480281974557594367601653e-8,
        1.248717148923965943024412876132e-1,
    )

    var rtol: Scalar[type]

    @parameter
    if type == DType.float32:
        rtol = 1e-6
    else:  # type == DType.float64
        rtol = 1e-12

    unit_test.assert_all_close(
        specials.lgamma1p(x), expected, atol=0.0, rtol=rtol
    )


fn test_lgamma1p_edge_cases[type: DType]() raises:
    var unit_test = UnitTest("test_lgamma1p_edge_cases_" + str(type))

    var inf = math.inf[type]()
    var nan = math.nan[type]()
    var x = SIMD[type, 4](nan, -1.0, 0.0, inf)

    var expected = math.lgamma(1.0 + x)
    var actual = specials.lgamma1p(x)

    unit_test.assert_equal(actual, expected)


fn test_lbeta() raises:
    var unit_test = UnitTest("test_lbeta")

    var x = SIMD[DType.float64, 4](1.0, 8.0, 1e-5, 1e4)
    var y = SIMD[DType.float64, 4](7.0, 8.0, 1e10, 1e5)

    # The expected values were computed using `mpmath`.
    var expected = SIMD[DType.float64, 4](
        -1.9459101490553133051,
        -10.848948661710062966,
        11.5126894343865267210,
        -33513.609276569981795,
    )

    unit_test.assert_all_close(
        specials.lbeta(x, y), expected, atol=0.0, rtol=1e-06
    )


fn test_lbeta_edge_cases() raises:
    var unit_test = UnitTest("test_lbeta_edge_cases")

    var neg = SIMD[DType.float64, 1](-1.0)
    var inf = math.inf[DType.float64]()
    var nan = math.nan[DType.float64]()
    var x = SIMD[DType.float64, 8](neg, neg, neg, neg, 0.0, 0.0, 1.0, 1.0)
    var y = SIMD[DType.float64, 8](0.0, 2.0, nan, inf, 2.0, inf, nan, inf)

    # The expected values were computed using the R language.
    var expected = SIMD[DType.float64, 8](
        nan, nan, nan, nan, inf, inf, nan, -inf
    )
    var actual = specials.lbeta(x, y)

    unit_test.assert_equal(actual, expected)


fn _mp_rgamma1pm1[type: DType](x: Scalar[type]) raises -> Scalar[type]:
    var mp = Python.import_module("mpmath")
    mp.mp.dps += 300

    var result = mp.mpf(1) / mp.gamma(mp.mpf(1) + mp.mpf(x)) - mp.mpf(1)

    mp.mp.dps -= 300
    return result.to_float64().cast[type]()


fn test_rgamma1pm1[type: DType]() raises:
    var unit_test = UnitTest("test_rgamma1pm1_" + str(type))

    var tiny = FloatLimits[type].min()
    var epsneg = FloatLimits[type].epsilon_neg()
    var eps = FloatLimits[type].epsilon()

    var xs = StaticTuple[Scalar[type], 10](
        -1.0 + epsneg,
        -0.5,
        -tiny,
        0.0,
        tiny,
        0.5,
        1.0 - epsneg,
        1.0,
        1.5 - eps,
        7.0,
    )

    var rtol: Scalar[type]

    @parameter
    if type == DType.float32:
        rtol = 1e-6
    else:  # type == DType.float64
        rtol = 1e-12

    for i in range(len(xs)):
        var x = xs[i]
        var expected = _mp_rgamma1pm1[type](x)
        var actual = specials.rgamma1pm1(x)

        unit_test.assert_all_close(actual, expected, atol=0.0, rtol=rtol)


fn test_rgamma1pm1_special_cases[type: DType]() raises:
    var unit_test = UnitTest("test_rgamma1pm1_special_cases_" + str(type))

    var inf = math.inf[type]()
    var nan = math.nan[type]()
    var x = SIMD[type, 4](nan, -inf, inf, nan)

    var expected = SIMD[type, 4](nan, nan, -1, nan)
    var actual = specials.rgamma1pm1(x)

    unit_test.assert_equal(actual, expected)


fn main() raises:
    # Setting the mpmath precision for this module
    var mp = Python.import_module("mpmath")
    mp.mp.dps = 50

    # lgamma_correction
    test_lgamma_correction[DType.float64]()
    test_lgamma_correction[DType.float32]()

    test_lgamma_correction_special_cases[DType.float64]()
    test_lgamma_correction_special_cases[DType.float32]()

    # lgamma1p
    test_lgamma1p_region1[DType.float32]()
    test_lgamma1p_region1[DType.float64]()

    test_lgamma1p_region2[DType.float32]()
    test_lgamma1p_region2[DType.float64]()

    test_lgamma1p_edge_cases[DType.float32]()
    test_lgamma1p_edge_cases[DType.float64]()

    # lbeta
    test_lbeta()
    test_lbeta_edge_cases()

    # rgamma1pm1
    test_rgamma1pm1[DType.float64]()
    test_rgamma1pm1[DType.float32]()

    test_rgamma1pm1_special_cases[DType.float64]()
    test_rgamma1pm1_special_cases[DType.float32]()


--- test/test_test_utils/test_numerics_testing.py ---

# type: ignore
# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
# RUN: %pytest %s

"""Tests for numerical testing utilities."""

import numpy as np
import pytest

from test_utils import numerics_testing


@pytest.mark.parametrize(
    "result_dtype, truth_dtype",
    [
        (np.float32, np.dtype("O")),
        (np.float32, np.float64),
        (np.float64, np.dtype("O")),
        (np.float64, np.longdouble),
    ],
)
def test_relative_error(result_dtype, truth_dtype):
    result = np.array([0.0, 1.1, 2.0, 3.0], dtype=result_dtype)
    truth = np.array([0.0, 1.0, 0.0, 3.0], dtype=truth_dtype)

    output_dtype = numerics_testing._promote_dtype(result_dtype)
    actual = numerics_testing.relative_error(result, truth)
    expected = np.array([0.0, 0.1, np.inf, 0.0], dtype=output_dtype)

    np.testing.assert_equal(actual.dtype, output_dtype)
    if result_dtype == np.float32:
        np.testing.assert_allclose(actual, expected, rtol=1e-6, atol=0.0)
    else:
        np.testing.assert_allclose(actual, expected, rtol=1e-15, atol=0.0)


def test_relative_error_invalid_result_dtype():
    result = np.array([0.0, 1.1, 2.0, 3.0], dtype=np.float16)
    truth = np.array([0.0, 1.0, 0.0, 3.0], dtype=np.float32)

    with pytest.raises(TypeError, match="single .* or double"):
        _ = numerics_testing.relative_error(result, truth)


def test_relative_error_invalid_truth_dtype():
    result = np.array([0.0, 1.1, 2.0, 3.0], dtype=np.float32)
    truth = np.array([0.0, 1.0, 0.0, 3.0], dtype=np.float32)

    with pytest.raises(TypeError, match="higher precision"):
        _ = numerics_testing.relative_error(result, truth)


@pytest.mark.parametrize(
    "x, target_dtype, expected",
    [
        (
            np.array(
                [np.nan, -1.0, 0.0, 1.0, 5.0, np.finfo(np.float64).max, np.inf],
                dtype=np.longdouble,
            ),
            np.float64,
            np.array(
                [
                    np.nan,
                    1.1102230246251565404e-16,
                    4.940656458412465442e-324,
                    1.1102230246251565404e-16,
                    8.8817841970012523234e-16,
                    1.9958403095347198117e292,
                    1.9958403095347198117e292,
                ],
                dtype=np.longdouble,
            ),
        ),
        (
            np.array(
                [np.nan, -1.0, 0.0, 1.0, 5.0, np.finfo(np.float32).max, np.inf],
                dtype=np.float64,
            ),
            np.float32,
            np.array(
                [
                    np.nan,
                    5.960464477539063e-08,
                    1.401298464324817e-45,
                    5.960464477539063e-08,
                    4.76837158203125e-07,
                    2.028240960365167e31,
                    2.028240960365167e31,
                ],
                dtype=np.float64,
            ),
        ),
    ],
)
def test_kahan_ulp(x, target_dtype, expected):
    output_dtype = numerics_testing._promote_dtype(target_dtype)
    actual = numerics_testing.kahan_ulp(x, target_dtype)

    np.testing.assert_equal(actual.dtype, output_dtype)
    np.testing.assert_allclose(actual, expected, rtol=1e-15, atol=0.0)


def test_kahan_ulp_invalid_x_dtype():
    x = np.array([0.0, 1.0], dtype=np.float32)
    target_dtype = np.float32

    with pytest.raises(TypeError, match="higher precision"):
        numerics_testing.kahan_ulp(x, target_dtype)


def test_kahan_ulp_invalid_target_dtype():
    x = np.array([0.0, 1.0], dtype=np.float64)
    target_dtype = np.int32

    with pytest.raises(TypeError, match="single .* or double"):
        numerics_testing.kahan_ulp(x, target_dtype)


@pytest.mark.parametrize(
    "result, truth, expected",
    [
        (
            np.float64(1.0) + np.finfo(np.float64).eps,
            np.longdouble(1.0),
            np.longdouble(2.0),
        ),
        (
            np.float64(1.0) + np.finfo(np.float64).eps,
            np.int64(1.0),
            np.longdouble(2.0),
        ),
        (
            np.float32(1.0) + np.finfo(np.float32).eps,
            np.float64(1.0),
            np.float64(2.0),
        ),
        (
            np.float32(1.0) + np.finfo(np.float32).eps,
            np.int32(1.0),
            np.float64(2.0),
        ),
    ],
)
def test_error_in_ulps(result, truth, expected):
    output_dtype = numerics_testing._promote_dtype(result.dtype)
    actual = numerics_testing.error_in_ulps(result, truth)

    np.testing.assert_equal(actual.dtype, output_dtype)
    np.testing.assert_allclose(actual, expected, rtol=1e-15, atol=0.0)


def test_error_in_ulps_invalid_result_dtype():
    result = np.int32(1)
    truth = np.float64(1.0)

    with pytest.raises(TypeError, match="single .* or double"):
        numerics_testing.error_in_ulps(result, truth)


def test_error_in_ulps_invalid_truth_dtype():
    result = np.float64(1.0)
    truth = np.float32(1.0)

    with pytest.raises(TypeError, match="higher precision"):
        numerics_testing.error_in_ulps(result, truth)


--- test/test_test_utils/test_tensor.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2023 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
# RUN: %mojo %build_dir %debug_level %sanitize_checks %s

"""Tests for tensor utilities."""

import math

from tensor import Tensor, TensorShape

from test_utils import UnitTest
from test_utils.tensor import elementwise, random_uniform


# TODO: Investigate why defining `MOJO_ENABLE_ASSERTIONS` leads to test failures.


@always_inline
fn reciprocal(x: SIMD) -> __type_of(x):
    return 1.0 / x


@always_inline
fn div(x: SIMD, y: __type_of(x)) -> __type_of(x):
    return x / y


fn test_unary_elementwise() raises:
    @parameter
    fn test_fn[type: DType, force_sequential: Bool](*shape: Int) raises:
        var unit_test = UnitTest("test_unary_elementwise")

        var x = random_uniform[type](1.0, 2.0, TensorShape(shape))

        var actual = elementwise[reciprocal](x)
        var expected = 1.0 / x

        unit_test.assert_true(
            actual == expected, msg=str(x.spec()) + "_" + str(force_sequential)
        )

    test_fn[DType.float32, force_sequential=False](64, 64)
    test_fn[DType.float32, force_sequential=True](64, 64)

    test_fn[DType.float64, force_sequential=False](64, 64)
    test_fn[DType.float64, force_sequential=True](64, 64)


fn test_binary_scalar_elementwise() raises:
    @parameter
    fn test_fn[type: DType, force_sequential: Bool](*shape: Int) raises:
        var unit_test = UnitTest("test_binary_scalar_elementwise")

        var x = random_uniform[type](1.0, 2.0, TensorShape(shape))

        var actual = elementwise[div](x, 2.0)
        var expected = x / 2.0

        unit_test.assert_true(
            actual == expected, msg=str(x.spec()) + "_" + str(force_sequential)
        )

    test_fn[DType.float32, force_sequential=False](64, 64)
    test_fn[DType.float32, force_sequential=True](64, 64)

    test_fn[DType.float64, force_sequential=False](64, 64)
    test_fn[DType.float64, force_sequential=True](64, 64)


fn test_binary_elementwise() raises:
    @parameter
    fn test_fn[type: DType, force_sequential: Bool](*shape: Int) raises:
        var unit_test = UnitTest("test_binary_elementwise")

        var x = random_uniform[type](1.0, 2.0, TensorShape(shape))
        var y = random_uniform[type](1.0, 2.0, x.shape())

        var actual = elementwise[div](x, y)
        var expected = x / y

        unit_test.assert_true(
            actual == expected, msg=str(x.spec()) + "_" + str(force_sequential)
        )

    test_fn[DType.float32, force_sequential=False](64, 64)
    test_fn[DType.float32, force_sequential=True](64, 64)

    test_fn[DType.float64, force_sequential=False](64, 64)
    test_fn[DType.float64, force_sequential=True](64, 64)


fn main() raises:
    test_unary_elementwise()
    test_binary_elementwise()
    test_binary_scalar_elementwise()


--- test/test_test_utils/test_testing.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2023 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
# RUN: %mojo %build_dir %assertion_flag %debug_level %sanitize_checks %s

"""Tests for testing utilities."""

import math

from testing import assert_raises

from test_utils import UnitTest


fn test_assert_true_success() raises:
    var unit_test = UnitTest("test_assert_true_success")
    unit_test.assert_true(True, msg="this test should not have failed")


fn test_assert_true_failure() raises:
    var unit_test = UnitTest("test_assert_true_failure")
    with assert_raises(contains="this test should have failed"):
        unit_test.assert_true(False, msg="this test should have failed")


fn test_assert_equal_int_success() raises:
    var unit_test = UnitTest("test_assert_equal_int_success")
    unit_test.assert_equal(0, 0, msg="this test should not have failed")


fn test_assert_equal_int_failure() raises:
    var unit_test = UnitTest("test_assert_equal_int_failure")
    with assert_raises(contains="this test should have failed"):
        unit_test.assert_equal(0, 1, msg="this test should have failed")


fn test_assert_equal_simd_success() raises:
    var unit_test = UnitTest("test_assert_equal_simd_success")

    var nan = math.nan[DType.float32]()
    var inf = math.inf[DType.float32]()
    var actual = SIMD[DType.float32, 4](1.0, 2.0, inf, nan)
    var desired = SIMD[DType.float32, 4](1.0, 2.0, inf, nan)

    unit_test.assert_equal(
        actual, desired, msg="this test should not have failed"
    )


fn test_assert_equal_simd_failure() raises:
    var unit_test = UnitTest("test_assert_equal_simd_failure")

    var nan = math.nan[DType.float32]()
    var inf = math.inf[DType.float32]()
    var actual = SIMD[DType.float32, 4](0.0, 2.0, inf, nan)
    var desired = SIMD[DType.float32, 4](0.0, 2.0, inf, 4.0)

    with assert_raises(contains="this test should have failed"):
        unit_test.assert_equal(
            actual, desired, msg="this test should have failed"
        )


fn test_assert_all_close_success() raises:
    var unit_test = UnitTest("test_assert_all_close_success")

    var nan = math.nan[DType.float32]()
    var inf = math.inf[DType.float32]()
    var actual = SIMD[DType.float32, 4](nan, inf, 1.1, 10.2)
    var desired = SIMD[DType.float32, 4](nan, inf, 1.0, 10.0)

    unit_test.assert_all_close(
        actual,
        desired,
        atol=0.1,
        rtol=1.0e-2,
        msg="this test should not have failed",
    )


fn test_assert_all_close_failure_nan() raises:
    var unit_test = UnitTest("test_assert_all_close_failure_nan")

    var nan = math.nan[DType.float32]()
    var inf = math.inf[DType.float32]()
    var actual = SIMD[DType.float32, 4](nan, inf, 1.1, 10.2)
    var desired = SIMD[DType.float32, 4](0.0, inf, 1.0, 10.0)

    with assert_raises(contains="this test should have failed"):
        unit_test.assert_all_close(
            actual,
            desired,
            atol=0.1,
            rtol=1.0e-2,
            msg="this test should have failed",
        )


fn test_assert_all_close_failure_inf() raises:
    var unit_test = UnitTest("test_assert_all_close_failure_inf")

    var nan = math.nan[DType.float32]()
    var inf = math.inf[DType.float32]()
    var actual = SIMD[DType.float32, 4](nan, 0.0, 1.1, 10.2)
    var desired = SIMD[DType.float32, 4](nan, inf, 1.0, 10.0)

    with assert_raises(contains="this test should have failed"):
        unit_test.assert_all_close(
            actual,
            desired,
            atol=0.1,
            rtol=1.0e-2,
            msg="this test should have failed",
        )


fn test_assert_all_close_failure_atol() raises:
    var unit_test = UnitTest("test_assert_all_close_failure_atol")

    var nan = math.nan[DType.float32]()
    var inf = math.inf[DType.float32]()
    var actual = SIMD[DType.float32, 4](nan, inf, 1.11, 10.2)
    var desired = SIMD[DType.float32, 4](nan, inf, 1.0, 10.0)

    with assert_raises(contains="this test should have failed"):
        unit_test.assert_all_close(
            actual,
            desired,
            atol=0.1,
            rtol=1.0e-2,
            msg="this test should have failed",
        )


fn test_assert_all_close_failure_rtol() raises:
    var unit_test = UnitTest("test_assert_all_close_failure_rtol")

    var nan = math.nan[DType.float32]()
    var inf = math.inf[DType.float32]()
    var actual = SIMD[DType.float32, 4](nan, inf, 1.1, 10.21)
    var desired = SIMD[DType.float32, 4](nan, inf, 1.0, 10.0)

    with assert_raises(contains="this test should have failed"):
        unit_test.assert_all_close(
            actual,
            desired,
            atol=0.1,
            rtol=1.0e-2,
            msg="this test should have failed",
        )


fn main() raises:
    test_assert_true_success()
    test_assert_true_failure()

    test_assert_equal_int_success()
    test_assert_equal_int_failure()

    test_assert_equal_simd_success()
    test_assert_equal_simd_failure()

    test_assert_all_close_success()
    test_assert_all_close_failure_nan()
    test_assert_all_close_failure_inf()
    test_assert_all_close_failure_atol()
    test_assert_all_close_failure_rtol()


--- test/test_utils/__init__.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #

"""Utilities for testing Specials code."""

from .testing import UnitTest


--- test/test_utils/__init__.py ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #

"""Utilities for testing Specials code."""

from .python import benchmark
from .python import numerics_testing


--- test/test_utils/benchmark.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #

import random

from python import Python
from python.object import PythonObject
from sys.info import simdwidthof
from tensor import Tensor
from utils.static_tuple import StaticTuple

from test_utils.tensor import (
    elementwise,
    random_uniform,
    run_benchmark,
    tensor_to_numpy_array,
    UnaryOperator,
)


fn _solution_report[
    solution_name: StringLiteral,
    func: UnaryOperator,
    *,
    type: DType,
    simd_width: Int,
    force_sequential: Bool = False,
](x: Tensor[type], truth: PythonObject) raises -> PythonObject:
    """Computes the evaluation metrics for a numerical solution in Mojo."""
    var builtins = Python.import_module("builtins")
    var np = Python.import_module("numpy")
    var py_utils = Python.import_module("test_utils")

    var result = elementwise[func, type=type, simd_width=simd_width](x)
    var msecs = run_benchmark[
        func,
        type=type,
        simd_width=simd_width,
        force_sequential=force_sequential,
    ](x).mean("ms")
    var err = py_utils.numerics_testing.error_in_ulps(
        tensor_to_numpy_array(result), truth
    )

    var report = builtins.list()
    _ = report.append(solution_name)
    _ = report.append(py_utils.benchmark.format_float(np.max(err)))
    _ = report.append(
        py_utils.benchmark.format_float(np.sqrt(np.mean(np.square(err))))
    )
    _ = report.append(py_utils.benchmark.format_float(msecs))

    return report


fn run_experiment[
    *,
    num_domains: Int,
    specials_func: UnaryOperator,
    mojo_func: UnaryOperator,
    type: DType,
    simd_width: Int = simdwidthof[type](),
    force_sequential: Bool = False,
    mojo_func_name: StringLiteral = "Mojo Stdlib",
](
    experiment_name: StringLiteral,
    *,
    num_samples: Int,
    min_values: StaticTuple[Scalar[type], num_domains],
    max_values: StaticTuple[Scalar[type], num_domains],
    truth_func: PythonObject,
    python_func: PythonObject,
    python_func_name: StringLiteral = "Python",
) raises:
    """Runs a given experiment."""
    var builtins = Python.import_module("builtins")
    var py_utils = Python.import_module("test_utils")

    random.seed(42)

    var domain_names = builtins.list()
    var data = builtins.list()

    var num_solutions = 3

    for i in range(len(max_values)):
        var min_value = min_values[i]
        var max_value = max_values[i]

        _ = domain_names.append(
            py_utils.benchmark.format_domain_name(min_value, max_value)
        )

        var a = random_uniform[type, simd_width=simd_width](
            min_value, max_value, num_samples
        )
        var a_arr = tensor_to_numpy_array(a)

        # Truth function
        var truth = truth_func(a_arr)

        # Specials function
        var specials_report = _solution_report[
            "Specials",
            specials_func,
            type=type,
            simd_width=simd_width,
            force_sequential=force_sequential,
        ](a, truth)
        _ = data.append(specials_report)

        # Mojo function
        var mojo_report = _solution_report[
            mojo_func_name,
            mojo_func,
            type=type,
            simd_width=simd_width,
            force_sequential=force_sequential,
        ](a, truth)
        _ = data.append(mojo_report)

        # Python function
        var python_report = py_utils.benchmark.solution_report(
            python_func_name, python_func, a_arr, truth
        )
        _ = data.append(python_report)

    _ = py_utils.benchmark.print_experiment_results(
        data,
        domain_names,
        num_solutions,
        String(experiment_name) + " (" + str(type) + ")",
    )


--- test/test_utils/python/__init__.py ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #


--- test/test_utils/python/benchmark.py ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #

from timeit import timeit

import numpy as np

from tabulate import tabulate, SEPARATING_LINE  # type: ignore[import-untyped]

from test_utils.python import numerics_testing


def format_domain_name(min_value, max_value):
    """Formats domain name for printing."""
    values = [min_value, max_value]
    formatted = []

    for value in values:
        if value == int(value):
            if np.abs(value) > 10e3:
                formatted.append(f"{int(value):.0e}")
            else:
                formatted.append(f"{int(value)}")
        else:
            if np.abs(value) < 0.001:
                formatted.append(f"{value:.0e}")
            elif np.log10(np.abs(value)) >= 3:
                formatted.append(f"{value:.1e}")
            else:
                formatted.append(f"{value:.1f}")

    return f"{formatted[0]},{formatted[1]}"


def format_float(value):
    """Formats float values for printing."""
    if (value == 0.0) or (0.01 <= np.abs(value) < 1e9):
        return f"{value:,.3f}"
    else:
        return f"{value:.2e}"


def benchmark(func, *args):
    """Computes the average execution time of a Python function."""
    # Warmup phase
    _ = timeit(lambda: func(*args), number=2)

    msecs = 1000 * timeit(lambda: func(*args), number=100) / 100
    return msecs


def solution_report(solution_name, func, x_arr, truth):
    """Computes the evaluation metrics for a numerical solution in Python."""
    result = func(x_arr)
    msecs = benchmark(func, x_arr)
    err = numerics_testing.error_in_ulps(result, truth)

    return [
        solution_name,
        format_float(np.max(err)),
        format_float(np.sqrt(np.mean(np.square(err)))),
        format_float(msecs),
    ]


def print_experiment_results(data, domain_names, num_solutions, experiment_name):
    """Prints the evaluation metrics for all numerical solutions."""
    headers = [
        "\nDomain",
        "\nSolution",
        "Maximum Error\nObserved (ulps)",
        "RMS Error\nObserved (ulps)",
        "Average Execution\nTime (msecs)",
    ]

    # Insert domain names
    current_domain = 0
    for i, report in enumerate(data):
        if i % num_solutions == 0:
            domain_name = domain_names[current_domain]
            data[i].insert(0, domain_name)
            current_domain += 1
        else:
            data[i].insert(0, "")

    # Insert horizontal lines between domains
    for index in range(num_solutions, len(data) + num_solutions, num_solutions + 1):
        data.insert(index, SEPARATING_LINE)

    print(f"\nExperiment: {experiment_name}\n")

    colalign = ("left", "left", "right", "right", "right")
    table = tabulate(
        data, headers, tablefmt="simple", colalign=colalign, disable_numparse=True
    )

    print(table)


--- test/test_utils/python/numerics_testing.py ---

# ===----------------------------------------------------------------------=== #
# Copyright 2023 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
#
# Some of the code in this file is adapted from:
#
# tensorflow/probability:
# Copyright 2018 The TensorFlow Probability Authors. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License").
#
# References:
#
# Beebe, N. H. (2017). The Mathematical-Function Computation Handbook:
#   Programming Using the MathCW Portable Software Library.
# Springer International Publishing.
# https://doi.org/10.1007/978-3-319-64110-2
#
# Muller, J. M. (2005). On the definition of ulp (x).
# [Research Report] Laboratoire de l’informatique duparallélisme. 2005, 2+11p.
# https://inria.hal.science/inria-00070503/file/RR2005-09.pdf

"""Numerical testing utilities."""

import numpy as np


def _promote_dtype(dtype):
    """Promotes `dtype` to a higher precision floating-point type."""
    dtype = np.dtype(dtype)

    assert dtype in (np.float32, np.float64)

    if dtype == np.float32:
        return np.float64

    return np.longdouble


def relative_error(result, truth):
    """Computes the relative error of `result` relative to `truth`.

    The relative error is defined as `abs((result - truth) / truth)`. The
    computation of that difference and ratio are done in the next higher
    precision compared to the working precision.

    The data types `result.dtype` and `truth.dtype` must correspond to the
    working precision and a higher precision floating-point types, respectively.

    Args:
        result: A NumPy array of values whose deviation to assess.
        truth: A NumPy Array of values presumed correct. Must broadcast with
            `result`.

    Returns:
        A NumPy array of element-wise relative error values. The data type of
        the output array is the next higher precision floating-point type
        compared to the data type of `result`:
            - `np.float64` if `result.dtype` is `np.float32`; or
            - `np.longdouble` if `result.dtype` is `np.float64`.

    Raises:
        TypeError: If `result.dtype` is not a floating-point of single
            (`float32`) or double (`float64`) precision.
        TypeError: If `truth.dtype` is a floating-point type but does not have
            higher precision than `result.dtype`.
    """
    result = np.array(result)

    if result.dtype not in (np.float32, np.float64):
        raise TypeError(
            "`result.dtype` must be a floating-point of single (`float32`) or "
            "double (`float64`) precision"
        )

    output_dtype = _promote_dtype(result.dtype)
    truth = np.array(truth)

    if truth.dtype.kind == "f":
        if np.finfo(truth.dtype).precision <= np.finfo(result.dtype).precision:
            raise TypeError(
                "When `truth.dtype` is a floating-point type, it must have "
                "higher precision than `result.dtype`"
            )

    result = result.astype(output_dtype)
    truth = truth.astype(output_dtype)

    truth_is_zero = np.equal(truth, 0.0)
    largest_subnormal = np.nextafter(np.finfo(truth.dtype).smallest_normal, 0.0)
    truth_is_subnormal = ~truth_is_zero & (np.abs(truth) <= largest_subnormal)

    safe_denominator = np.where(
        truth_is_zero,
        np.ones_like(truth),
        np.where(
            (result > 0) & (truth > 0) & truth_is_subnormal,
            largest_subnormal,
            truth,
        ),
    )

    relerr = np.abs((result - truth) / safe_denominator)
    relerr = np.where(truth_is_zero, np.inf, relerr)
    relerr = np.where(result == truth, np.zeros_like(truth), relerr)

    return relerr


def kahan_ulp(x, target_dtype):
    """Computes the Kahan-ulp function at `x` element-wise.

    Ulp stands for "unit in the last place".

    If `x` is an arbitrary real number, the Kahan-ulp at `x` is the distance
    between the two floating-point numbers nearest `x`, even if `x` is not
    contained in the interval between them.

    The computation of the Kahan-ulp is done in the next higher precision
    compared to the working precision.

    Args:
        x: Array of floating-point values. The data type of this array must
            have higher precision than `target_dtype`.
        target_dtype: Data type corresponding to the working precision.

    Returns:
        Array of element-wise Kahan-ulp values. The data type of the output
        array is the next higher precision floating-point type compared to
        the data type of `x`:
            - `np.float64` if `result.dtype` is `np.float32`; or
            - `np.longdouble` if `result.dtype` is `np.float64`.

    Raises:
        TypeError: If `target_dtype` is not a floating-point of single
            (`float32`) or double (`float64`) precision.
        TypeError: If `x.dtype` is a floating-point type but does not have
            higher precision than `target_dtype`.
    """
    target_dtype = np.dtype(target_dtype)
    if target_dtype not in (np.float32, np.float64):
        raise TypeError(
            "`target_dtype` must be a floating-point of single (`float32`) or "
            "double (`float64`) precision"
        )

    x = np.array(x)
    if x.dtype.kind == "f":
        if np.finfo(x.dtype).precision <= np.finfo(target_dtype).precision:
            raise TypeError(
                "When `x.dtype` is a floating-point type, it must have higher "
                "precision than `target_dtype`"
            )

    higher_precision_dtype = _promote_dtype(target_dtype)
    x = x.astype(higher_precision_dtype)

    x = np.abs(x)
    one = higher_precision_dtype(1)

    emax = np.finfo(target_dtype).maxexp
    emin = np.finfo(target_dtype).minexp + 1
    t = np.finfo(target_dtype).nmant
    xsml = np.ldexp(one, emin)
    xmax = np.finfo(target_dtype).max

    is_normal = (x >= xsml) & (x <= xmax)

    result = np.empty_like(x)
    result = np.where(np.isnan(x), np.nan, result)
    result = np.where(x < xsml, np.ldexp(one, emin - (t + 1)), result)
    result = np.where(x > xmax, np.ldexp(one, emax - (t + 1)), result)

    safe_x = np.where(is_normal, x, one)
    expmin = np.log2(safe_x).astype(np.int32)
    exponent = expmin - t

    powermin = np.ldexp(one, expmin)
    exponent = np.where(
        safe_x / powermin <= one + np.ldexp(one, -(t + 2)),
        exponent - 1,
        exponent,
    )

    result = np.where(is_normal, np.ldexp(one, exponent), result)

    if result.shape == ():
        return higher_precision_dtype(result.item(0))

    return result


def error_in_ulps(result, truth):
    """Computes the error in ulps of `result` relative to `truth`.

    Ulp stands for "unit in the last place".

    The error in ulps is defined as `abs((result - truth) / kahan_ulp(truth))`.
    The computation of that difference and ratio are done in the next higher
    precision compared to the working precision.

    The data types `result.dtype` and `truth.dtype` must correspond to the
    working precision and a higher precision floating-point types, respectively.

    Args:
        result: Array of values whose deviation to assess.
        truth: Array of values presumed correct. Must broadcast with `result`.

    Returns:
        A NumPy array of element-wise error in ulps values. The data type of
        the output array is the next higher precision floating-point type
        compared to the data type of `result`:
            - `np.float64` if `result.dtype` is `np.float32`; or
            - `np.longdouble` if `result.dtype` is `np.float64`.

    Raises:
        TypeError: If `result.dtype` is not a floating-point of single
            (`float32`) or double (`float64`) precision.
        TypeError: If `truth.dtype` is a floating-point type but does not have
            higher precision than `result.dtype`.
    """
    result = np.array(result)

    if result.dtype not in (np.float32, np.float64):
        raise TypeError(
            "`result.dtype` must be a floating-point of single (`float32`) or "
            "double (`float64`) precision"
        )

    target_dtype = result.dtype
    output_dtype = _promote_dtype(target_dtype)
    truth = np.array(truth)

    if truth.dtype.kind == "f":
        if np.finfo(truth.dtype).precision <= np.finfo(result.dtype).precision:
            raise TypeError(
                "When `truth.dtype` is a floating-point type, it must have "
                "higher precision than `result.dtype`"
            )

    result = result.astype(output_dtype)
    truth = truth.astype(output_dtype)

    ulp_truth = kahan_ulp(truth, target_dtype=target_dtype)

    return np.abs((result - truth) / ulp_truth)


--- test/test_utils/tensor.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2023 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #

"""Utilities for working with Tensors."""

import benchmark
import math

from algorithm.functional import vectorize
from python import Python
from python.object import PythonObject
from tensor import Tensor, TensorShape

from specials.utils import functional


alias UnaryOperator = fn[type: DType, width: Int] (SIMD[type, width]) -> SIMD[
    type, width
]
"""
Signature of a function that performs an elementwise operation on a single SIMD
vector.
"""

alias BinaryOperator = fn[type: DType, width: Int] (
    SIMD[type, width], SIMD[type, width]
) -> SIMD[type, width]
"""
Signature of a function that performs an elementwise operation on two SIMD
vectors.
"""


# ===----------------------------------------------------------------------=== #
# Elementwise
# ===----------------------------------------------------------------------=== #


@always_inline
fn _elementwise_impl[
    func: UnaryOperator,
    type: DType,
    simd_width: Int,
    force_sequential: Bool,
](x: Tensor[type]) -> Tensor[type]:
    """Implements the elementwise operation on a tensor."""
    var result = Tensor[type](x.shape())
    var num_elements = x.num_elements()

    @always_inline
    @parameter
    fn inner_func[simd_width: Int](index: Int):
        var a = x.load[width=simd_width](index)
        result.store[width=simd_width](index, func(a))

    @parameter
    if force_sequential:
        vectorize[inner_func, simd_width](num_elements)
    else:
        functional.elementwise[inner_func, simd_width=simd_width](num_elements)

    return result^


@always_inline
fn _elementwise_impl[
    func: BinaryOperator,
    type: DType,
    simd_width: Int,
    force_sequential: Bool,
](x: Tensor[type], scalar: Scalar[type]) -> Tensor[type]:
    """Implements the elementwise operation on a tensor and a scalar."""
    var result = Tensor[type](x.shape())
    var num_elements = x.num_elements()

    @always_inline
    @parameter
    fn inner_func[simd_width: Int](index: Int):
        var a = x.load[width=simd_width](index)
        var b = SIMD[type, simd_width](scalar)
        result.store[width=simd_width](index, func(a, b))

    @parameter
    if force_sequential:
        vectorize[inner_func, simd_width](num_elements)
    else:
        functional.elementwise[inner_func, simd_width=simd_width](num_elements)

    return result^


@always_inline
fn _elementwise_impl[
    func: BinaryOperator,
    type: DType,
    simd_width: Int,
    force_sequential: Bool,
](x: Tensor[type], y: Tensor[type]) -> Tensor[type]:
    """Implements the elementwise operation on two tensors."""
    var result = Tensor[type](x.shape())
    var num_elements = x.num_elements()

    @always_inline
    @parameter
    fn inner_func[simd_width: Int](index: Int):
        var a = x.load[width=simd_width](index)
        var b = y.load[width=simd_width](index)
        result.store[width=simd_width](index, func(a, b))

    @parameter
    if force_sequential:
        vectorize[inner_func, simd_width](num_elements)
    else:
        functional.elementwise[inner_func, simd_width=simd_width](num_elements)

    return result^


fn elementwise[
    func: UnaryOperator,
    *,
    type: DType,
    simd_width: Int = simdwidthof[type](),
    force_sequential: Bool = False,
](x: Tensor[type]) -> Tensor[type]:
    """Applies an unary operator to a tensor element-wise.

    Constraints:
        The type must be a floating-point of single or double precision.

    Parameters:
        func: The unary operator to apply to the input.
        type: The input and output type.
        simd_width: The SIMD vector to use. Default is the vector size of the
             type on the host system.
        force_sequential: Whether to force sequential execution. Default is
            `False`.

    Args:
        x: The tensor.

    Returns:
        The result of applying the unary operator to the input.
    """
    constrained[
        type == DType.float32 or type == DType.float64,
        "tensor type must be a floating-point of single (`float32`) "
        + "or double (`float64`) precision.",
    ]()

    var result = _elementwise_impl[func, type, simd_width, force_sequential](x)
    return result^


fn elementwise[
    func: BinaryOperator,
    *,
    type: DType,
    simd_width: Int = simdwidthof[type](),
    force_sequential: Bool = False,
](x: Tensor[type], scalar: Scalar[type]) -> Tensor[type]:
    """Applies a binary operator to a tensor and a scalar element-wise.

    Constraints:
        The type must be a floating-point of single or double precision.

    Parameters:
        func: The binary operator to apply to the inputs.
        type: The input and output type.
        simd_width: The SIMD vector to use. Default is the vector size of the
             type on the host system.
        force_sequential: Whether to force sequential execution. Default is
            `False`.

    Args:
        x: The tensor.
        scalar: The scalar.

    Returns:
        The result of applying the binary operator to the inputs.
    """
    constrained[
        type == DType.float32 or type == DType.float64,
        "tensor type must be a floating-point of single (`float32`) "
        + "or double (`float64`) precision.",
    ]()

    var result = _elementwise_impl[func, type, simd_width, force_sequential](
        x, scalar
    )
    return result^


fn elementwise[
    func: BinaryOperator,
    *,
    type: DType,
    simd_width: Int = simdwidthof[type](),
    force_sequential: Bool = False,
](x: Tensor[type], y: Tensor[type]) raises -> Tensor[type]:
    """Applies a binary operator to two tensors element-wise.

    Constraints:
        The type must be a floating-point of single or double precision. And it
        will raise an exception if the arguments do not have the same shape.

    Parameters:
        func: The binary operator to apply to the inputs.
        type: The input and output type.
        simd_width: The SIMD width to use. Default is the vector size of the
            type on the host system.
        force_sequential: Whether to force sequential execution. Default is
            `False`.

    Args:
        x: The first tensor.
        y: The second tensor.

    Returns:
       The result of applying the binary operator to the inputs.
    """
    constrained[
        type == DType.float32 or type == DType.float64,
        "tensor type must be a floating-point of single (`float32`) "
        + "or double (`float64`) precision.",
    ]()

    if x.shape() != y.shape():
        raise Error("The arguments `x` and `y` must have the same shape.")

    var result = _elementwise_impl[func, type, simd_width, force_sequential](
        x, y
    )
    return result^


# ===----------------------------------------------------------------------=== #
# Benchmark
# ===----------------------------------------------------------------------=== #


fn run_benchmark[
    func: UnaryOperator,
    *,
    type: DType,
    simd_width: Int = simdwidthof[type](),
    force_sequential: Bool = False,
](
    x: Tensor[type],
    num_warmup: Int = 2,
    max_iters: Int = 100_000,
    min_runtime_secs: SIMD[DType.float64, 1] = 0.5,
    max_runtime_secs: SIMD[DType.float64, 1] = 1,
) -> benchmark.Report:
    """Runs a benchmark for a unary operator applied to a tensor element-wise.

    Benchmarking continues until `min_runtime_secs` has elapsed and either
    `max_iters` OR `max_runtime_secs` is achieved.

    Constraints:
        The type must be a floating-point of single or double precision.

    Parameters:
        func: The binary operator to apply to the input. This is the function
            that will be benchmarked.
        type: The input type.
        simd_width: The SIMD width to use. Default is the vector size of the
            type on the host system.
        force_sequential: Whether to force sequential execution. Default is
            `False`.

    Args:
        x: The tensor.
        num_warmup: Number of warmup iterations to run before starting
            benchmarking. Default is 2.
        max_iters: Max number of iterations to run. Default is 100_000.
        min_runtime_secs: Lower bound on benchmarking time in secs. Default
            is 0.5.
        max_runtime_secs: Upper bound on benchmarking time in secs. Default
            is 1.

    Returns:
        A report containing statistics of the benchmark.
    """

    @always_inline
    @parameter
    fn test_fn():
        _ = elementwise[
            func,
            type=type,
            simd_width=simd_width,
            force_sequential=force_sequential,
        ](x)

    return benchmark.run[test_fn](
        num_warmup=num_warmup,
        max_iters=max_iters,
        min_runtime_secs=min_runtime_secs,
        max_runtime_secs=max_runtime_secs,
    )


fn run_benchmark[
    func: BinaryOperator,
    *,
    type: DType,
    simd_width: Int = simdwidthof[type](),
    force_sequential: Bool = False,
](
    x: Tensor[type],
    y: Tensor[type],
    num_warmup: Int = 2,
    max_iters: Int = 100_000,
    min_runtime_secs: SIMD[DType.float64, 1] = 0.5,
    max_runtime_secs: SIMD[DType.float64, 1] = 1,
) raises -> benchmark.Report:
    """
    Runs a benchmark for a binary operator applied to two tensors element-wise.

    Benchmarking continues until `min_runtime_secs` has elapsed and either
    `max_iters` OR `max_runtime_secs` is achieved.

    Constraints:
        The type must be a floating-point of single or double precision. And it
        will raise an exception if the arguments do not have the same shape.

    Parameters:
        func: The binary operator to apply to the inputs. This is the function
            that will be benchmarked.
        type: The input type.
        simd_width: The SIMD width to use. Default is the vector size of the
            type on the host system.
        force_sequential: Whether to force sequential execution. Default is
            `False`.

    Args:
        x: The first tensor.
        y: The second tensor.
        num_warmup: Number of warmup iterations to run before starting
            benchmarking. Default is 2.
        max_iters: Max number of iterations to run. Default is 100_000.
        min_runtime_secs: Lower bound on benchmarking time in secs. Default
            is 0.5.
        max_runtime_secs: Upper bound on benchmarking time in secs. Default
            is 1.

    Returns:
        A report containing statistics of the benchmark.
    """
    if x.shape() != y.shape():
        raise Error("The arguments `x` and `y` must have the same shape.")

    @always_inline
    @parameter
    fn test_fn():
        try:
            _ = elementwise[
                func,
                type=type,
                simd_width=simd_width,
                force_sequential=force_sequential,
            ](x, y)
        except Error:
            pass

    return benchmark.run[test_fn](
        num_warmup=num_warmup,
        max_iters=max_iters,
        min_runtime_secs=min_runtime_secs,
        max_runtime_secs=max_runtime_secs,
    )


# ===----------------------------------------------------------------------=== #
# Random
# ===----------------------------------------------------------------------=== #


fn random_uniform[
    type: DType, *, simd_width: Int = simdwidthof[type]()
](
    min_value: Scalar[type],
    max_value: Scalar[type],
    owned shape: TensorShape,
) raises -> Tensor[type]:
    """Generates a tensor with random values drawn from a uniform distribution.

    Constraints:
        The type must be a floating-point of single or double precision. And it
        will raise an exception if `min_value >= max_value`.

    Parameters:
        type: The type of the tensor.
        simd_width: The SIMD width to use. Defaults to the vector size of the
            type on the host system.

    Args:
        min_value: The lower bound of the uniform distribution.
        max_value: The upper bound of the uniform distribution.
        shape: The tensor shape.

    Returns:
        The tensor with random values drawn from a uniform distribution.
    """
    constrained[
        type == DType.float32 or type == DType.float64,
        "tensor type must be a floating-point of single (`float32`) "
        + "or double (`float64`) precision.",
    ]()

    if min_value >= max_value:
        raise Error("`min_value` must be less than `max_value`.")

    return min_value + Tensor[type].rand(shape) * (max_value - min_value)


fn random_uniform[
    type: DType, *, simd_width: Int = simdwidthof[type]()
](
    min_value: Scalar[type],
    max_value: Scalar[type],
    *shape: Int,
) raises -> Tensor[type]:
    """Generates a tensor with random values drawn from a uniform distribution.

    Constraints:
        The type must be a floating-point of single or double precision. And it
        will raise an exception if `min_value >= max_value`.

    Parameters:
        type: The type of the tensor.
        simd_width: The SIMD width to use. Defaults to the vector size of the
            type on the host system.

    Args:
        min_value: The lower bound of the uniform distribution.
        max_value: The upper bound of the uniform distribution.
        shape: The tensor shape.

    Returns:
        The tensor with random values drawn from a uniform distribution.
    """
    return random_uniform[type, simd_width=simd_width](
        min_value, max_value, TensorShape(shape)
    )


# ===----------------------------------------------------------------------=== #
# NumPy
# ===----------------------------------------------------------------------=== #


fn tensor_to_numpy_array[type: DType](x: Tensor[type]) raises -> PythonObject:
    """Converts a tensor to a NumPy array.

    Constraints:
        Will raise an exception if it is not possible to convert the tensor to
        a NumPy array with the same shape, data type, and elements.

    Parameters:
        type: The type of the tensor.

    Args:
        x: The tensor.

    Returns:
        The NumPy array with the same shape, type, and elements as the tensor.
    """
    var builtins = Python.import_module("builtins")
    var np = Python.import_module("numpy")

    var shape = builtins.tuple(
        builtins.map(builtins.int, builtins.str(x.shape().__str__()).split("x"))
    )
    var num_elements = x.num_elements()

    var numpy_array = np.zeros(num_elements, type.__str__())

    for i in range(num_elements):
        _ = np.put(numpy_array, i, x[i])

    return numpy_array.reshape(shape)


--- test/test_utils/testing.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2023 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
#
# Some of the code in this file is adapted from:
#
# modularml/mojo
# Copyright (c) 2024, Modular Inc.
# Licensed under the Apache License v2.0 with LLVM Exceptions.
#
# numpy/numpy
# Copyright (c) 2005-2023, NumPy Developers.
# Licensed under BSD 3 clause.

"""Utilities for testing Specials code."""

import math
import testing


@value
struct UnitTest:
    """Provides utilities for testing Specials code."""

    var name: String
    """The name of the unit test."""

    fn __init__(inout self: Self, name: String):
        """Constructs a unit test with the given name.

        Args:
            name: The name of the unit test.
        """
        self.name = name

    fn _assert_true[T: Boolable](self: Self, val: T, *, msg: String) raises:
        """Base method for asserting that a value is `True`."""
        if not val.__bool__():
            raise Error("[" + self.name + "] AssertionError: " + msg)

    fn assert_true[T: Boolable](self: Self, val: T, *, msg: String = "") raises:
        """Asserts that the input value is `True`. If it is not then an `Error`
        is raised.

        Parameters:
            T: A type that can be converted to a bool.

        Args:
            val: The value to assert to be `True`.
            msg: The message to be printed if the assertion fails. Default is
                an empty string.

        Raises:
            An `Error` with the provided message if assert fails and `None`
            otherwise.
        """
        var err_msg: String = "condition was unexpectedly `False`"

        if msg:
            err_msg += " (" + msg + ")"

        self._assert_true(val, msg=err_msg)

    fn assert_equal(
        self: Self, actual: Int, desired: Int, *, msg: String = ""
    ) raises:
        """Asserts that the input values are equal. If it is not then an `Error`
        is raised.

        Args:
            actual: The actual integer.
            desired: The desired integer.
            msg: The message to be printed if the assertion fails. Default is
                an empty string.

        Raises:
            An `Error` with the provided message if assert fails and `None`
            otherwise.
        """
        var err_msg: String = str(actual) + " is not equal to " + str(desired)
        if msg:
            err_msg += " (" + msg + ")"

        self._assert_true(actual == desired, msg=err_msg)

    fn assert_equal[
        type: DType, width: Int
    ](
        self: Self,
        actual: SIMD[type, width],
        desired: SIMD[type, width],
        *,
        msg: String = "",
    ) raises:
        """Asserts that the input values are equal. If it is not then an `Error`
        is raised.

        In contrast to the standard usage in Mojo, `NaN`s are compared like
        numbers: no assertion is raised if both objects have `NaN`s in the
        same positions.

        Parameters:
            type: The type of the actual and desired SIMD vectors.
            width: The width of the actual and desired SIMD vectors.

        Args:
            actual: The actual SIMD vector.
            desired: The desired SIMD vector.
            msg: The message to be printed if the assertion fails. Default is
                an empty string.

        Raises:
            An `Error` with the provided message if assert fails and `None`
            otherwise.
        """
        var err_msg: String = str(actual) + " is not equal to " + str(desired)
        if msg:
            err_msg += " (" + msg + ")"

        var result = (actual == desired)

        @parameter
        if actual.type.is_floating_point():
            result |= math.isnan(actual) & math.isnan(desired)

        self._assert_true(all(result), msg=err_msg)

    fn assert_all_close[
        type: DType, width: Int
    ](
        self: Self,
        actual: SIMD[type, width],
        desired: SIMD[type, width],
        *,
        atol: Scalar[type] = 0.0,
        rtol: Scalar[type] = 1e-07,
        msg: String = "",
    ) raises:
        """Asserts that the input values are equal up to a tolerance. If it is
        not then an `Error` is raised.

        The test compares the difference between `actual` and `desired` to the
        sum of the absolute tolerance `atol` and the relative tolerance
        `rtol * abs(desired)`.

        In contrast to the standard usage in Mojo, `NaN`s are compared like
        numbers: no assertion is raised if both objects have `NaN`s in the same
        positions.

        Parameters:
            type: The type of the actual and desired SIMD vectors.
            width: The width of the actual and desired SIMD vectors.

        Args:
            actual: The actual SIMD vector.
            desired: The desired SIMD vector.
            atol: The absolute tolerance. Default is 0.0.
            rtol: The relative tolerance. Default is 1e-07.
            msg: The message to be printed if the assertion fails. Default is
                an empty string.

        Raises:
            An `Error` with the provided message if assert fails and `None`
            otherwise.
        """
        constrained[type.is_floating_point(), "type must be a floating-point"]()

        var diff = actual - desired
        var err_msg: String = str(actual) + " is not close to " + str(desired)
        err_msg += " with a diff of " + str(diff)
        if msg:
            err_msg += " (" + msg + ")"

        var result = (actual == desired)

        result |= math.isfinite(desired) & (
            abs(diff) <= (atol + rtol * abs(desired))
        )
        result |= math.isnan(actual) & math.isnan(desired)

        self._assert_true(all(result), msg=err_msg)


--- test/utils/test_big_int.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
# RUN: %mojo %build_dir %assertion_flag %debug_level %sanitize_checks %s

"""Tests for the `BigInt` struct."""

from builtin._location import __call_location
from testing.testing import _assert_equal_error, assert_equal

from specials.utils.big_int import BigInt, BigUInt
from test_utils import UnitTest


alias DEST_TYPE = DType.int64
alias DEFAULT_WIDTH = 1


@always_inline
fn _assert_equal[
    size: Int = DEFAULT_WIDTH
](
    lhs: BigInt[_, size=size, signed=_, word_type=_],
    rhs: SIMD[DEST_TYPE, size],
) raises:
    var casted_lhs = lhs.cast[DEST_TYPE]()

    if any(casted_lhs != rhs):
        raise _assert_equal_error(
            str(casted_lhs), str(rhs), "", __call_location()
        )


fn test_init() raises:
    _assert_equal(BigInt[8, size=1](), 0)
    _assert_equal(BigInt[24, size=1](), 0)
    _assert_equal(BigInt[8, size=4](), 0)
    _assert_equal(BigInt[24, size=4](), 0)

    _assert_equal(BigUInt[8, size=1](), 0)
    _assert_equal(BigUInt[24, size=1](), 0)
    _assert_equal(BigUInt[8, size=4](), 0)
    _assert_equal(BigUInt[24, size=4](), 0)


fn test_init_unsafe() raises:
    _ = BigInt[8, size=1](unsafe_uninitialized=True)
    _ = BigInt[24, size=1](unsafe_uninitialized=True)

    _ = BigUInt[8, size=1](unsafe_uninitialized=True)
    _ = BigUInt[24, size=1](unsafe_uninitialized=True)


fn test_init_from_int_literal() raises:
    _assert_equal(BigInt[8, size=1](0), 0)
    _assert_equal(BigInt[8, size=1](127), 127)
    _assert_equal(BigInt[8, size=1](-1), -1)
    _assert_equal(BigInt[8, size=1](-128), -128)
    _assert_equal(BigInt[24, size=1](0), 0)
    _assert_equal(BigInt[24, size=1](8_388_607), 8_388_607)
    _assert_equal(BigInt[24, size=1](-1), -1)
    _assert_equal(BigInt[24, size=1](-8_388_608), -8_388_608)

    _assert_equal(BigUInt[8, size=1](0), 0)
    _assert_equal(BigUInt[8, size=1](255), 255)
    _assert_equal(BigUInt[24, size=1](0), 0)
    _assert_equal(BigUInt[24, size=1](16_777_215), 16_777_215)


fn test_init_from_int() raises:
    _assert_equal(BigInt[8, size=1](Int(1)), 1)
    _assert_equal(BigInt[24, size=1](Int(1)), 1)

    _assert_equal(BigUInt[8, size=1](Int(255)), 255)
    _assert_equal(BigUInt[24, size=1](Int(16_777_215)), 16_777_215)


fn test_init_from_signed_simd() raises:
    alias WIDTH = 4
    alias VALUE = SIMD[DEST_TYPE, WIDTH](-2, -1, 1, 2)

    _assert_equal(BigInt[8](VALUE), VALUE)
    _assert_equal(BigInt[24](VALUE), VALUE)

    _assert_equal(
        BigUInt[8](VALUE),
        SIMD[DEST_TYPE, WIDTH](254, 255, 1, 2),
    )
    _assert_equal(
        BigUInt[24](VALUE),
        SIMD[DEST_TYPE, WIDTH](16_777_214, 16_777_215, 1, 2),
    )


fn test_init_from_unsigned_simd() raises:
    alias WIDTH = 4
    alias VALUE = SIMD[DType.uint64, WIDTH](1, 2, 255, 16_777_215)

    _assert_equal(
        BigInt[8](VALUE),
        SIMD[DEST_TYPE, WIDTH](1, 2, -1, -1),
    )
    _assert_equal(
        BigInt[24](VALUE),
        SIMD[DEST_TYPE, WIDTH](1, 2, 255, -1),
    )

    _assert_equal(
        BigUInt[8](VALUE),
        SIMD[DEST_TYPE, WIDTH](1, 2, 255, 255),
    )
    _assert_equal(
        BigUInt[24](VALUE),
        SIMD[DEST_TYPE, WIDTH](1, 2, 255, 16_777_215),
    )


fn test_explicit_copy() raises:
    alias BITS = List[Int](8, 24)
    alias SIGNED = List[Bool](True, False)

    @parameter
    for i in range(len(BITS)):
        alias bits = BITS[i]

        @parameter
        for j in range(len(SIGNED)):
            alias signed = SIGNED[j]

            var existing = BigInt[bits, size=2, signed=signed](1)
            var copy = BigInt(existing)

            assert_equal(existing.bits, copy.bits)
            assert_equal(existing.size, copy.size)
            assert_equal(existing.signed, copy.signed)
            assert_equal(existing.word_type, existing.word_type)

            _assert_equal(existing, 1)
            _assert_equal(copy, 1)

            copy += 1
            _assert_equal(existing, 1)
            _assert_equal(copy, 2)


fn test_min() raises:
    _assert_equal(BigInt[8, size=1].min(), -128)
    _assert_equal(BigInt[24, size=1].min(), -8_388_608)

    _assert_equal(BigUInt[8, size=1].min(), 0)
    _assert_equal(BigUInt[24, size=1].min(), 0)


fn test_max() raises:
    _assert_equal(BigInt[8, size=1].max(), 127)
    _assert_equal(BigInt[24, size=1].max(), 8_388_607)

    _assert_equal(BigUInt[8, size=1].max(), 255)
    _assert_equal(BigUInt[24, size=1].max(), 16_777_215)


fn test_add() raises:
    _assert_equal(BigInt[8, size=1](1) + BigInt[8, size=1](-2), -1)
    _assert_equal(BigInt[24, size=1](1) + BigInt[24, size=1](-2), -1)

    _assert_equal(BigUInt[8, size=1](15) + BigUInt[8, size=1](16), 31)
    _assert_equal(BigUInt[24, size=1](255) + BigUInt[24, size=1](1), 256)

    _assert_equal(
        BigUInt[24](SIMD[DEST_TYPE, 2](1, 2)) + 2,
        SIMD[DEST_TYPE, 2](3, 4),
    )


fn test_iadd() raises:
    var sval = BigInt[8, size=1](1)
    var uval = BigUInt[24](SIMD[DEST_TYPE, 2](0, 255))

    sval += BigInt[8, size=1](-2)
    _assert_equal(sval, -1)

    uval += 1
    _assert_equal(uval, SIMD[DEST_TYPE, 2](1, 256))


fn test_add_with_overflow() raises:
    var sval8 = BigInt[8](SIMD[DEST_TYPE, 2](-128, 127))
    var uval8 = BigUInt[8](SIMD[DEST_TYPE, 2](0, 255))

    assert_equal(sval8.add_with_overflow(1), SIMD[DType.bool, 2](False, True))
    _assert_equal(sval8, SIMD[DEST_TYPE, 2](-127, -128))

    assert_equal(sval8.add_with_overflow(-1), SIMD[DType.bool, 2](False, True))
    _assert_equal(sval8, SIMD[DEST_TYPE, 2](-128, 127))

    assert_equal(uval8.add_with_overflow(1), SIMD[DType.bool, 2](False, True))
    _assert_equal(uval8, SIMD[DEST_TYPE, 2](1, 0))

    var sval24 = BigInt[24](SIMD[DEST_TYPE, 2](-8_388_608, 8_388_607))
    var uval24 = BigUInt[24](SIMD[DEST_TYPE, 2](0, 16_777_215))

    assert_equal(sval24.add_with_overflow(1), SIMD[DType.bool, 2](False, True))
    _assert_equal(sval24, SIMD[DEST_TYPE, 2](-8_388_607, -8_388_608))

    assert_equal(sval24.add_with_overflow(-1), SIMD[DType.bool, 2](False, True))
    _assert_equal(sval24, SIMD[DEST_TYPE, 2](-8_388_608, 8_388_607))

    assert_equal(uval24.add_with_overflow(1), SIMD[DType.bool, 2](False, True))
    _assert_equal(uval24, SIMD[DEST_TYPE, 2](1, 0))


fn test_sub() raises:
    _assert_equal(BigInt[8, size=1](1) - BigInt[8, size=1](-2), 3)
    _assert_equal(BigInt[24](Int32(383)) - BigInt[24](Int32(-511)), 894)

    _assert_equal(BigUInt[8, size=1](16) - BigUInt[8, size=1](15), 1)
    _assert_equal(BigUInt[24, size=1](383) - BigUInt[24, size=1](255), 128)

    _assert_equal(
        BigInt[24, size=4](SIMD[DEST_TYPE, 4](1, 2, 3, 4)) - 2,
        SIMD[DEST_TYPE, 4](-1, 0, 1, 2),
    )


fn test_isub() raises:
    var sval = BigInt[8, size=1](1)
    var uval = BigUInt[24](SIMD[DEST_TYPE, 2](1, 256))

    sval -= BigInt[8, size=1](-2)
    _assert_equal(sval, 3)

    uval -= 1
    _assert_equal(uval, SIMD[DEST_TYPE, 2](0, 255))


fn test_sub_with_overflow() raises:
    var sval8 = BigInt[8](SIMD[DEST_TYPE, 2](-128, 127))
    var uval8 = BigUInt[8](SIMD[DEST_TYPE, 2](0, 255))

    assert_equal(sval8.sub_with_overflow(1), SIMD[DType.bool, 2](True, False))
    _assert_equal(sval8, SIMD[DEST_TYPE, 2](127, 126))

    assert_equal(sval8.sub_with_overflow(-1), SIMD[DType.bool, 2](True, False))
    _assert_equal(sval8, SIMD[DEST_TYPE, 2](-128, 127))

    assert_equal(uval8.sub_with_overflow(1), SIMD[DType.bool, 2](True, False))
    _assert_equal(uval8, SIMD[DEST_TYPE, 2](255, 254))

    var sval24 = BigInt[24](SIMD[DEST_TYPE, 2](-8_388_608, 8_388_607))
    var uval24 = BigUInt[24](SIMD[DEST_TYPE, 2](0, 16_777_215))

    assert_equal(sval24.sub_with_overflow(1), SIMD[DType.bool, 2](True, False))
    _assert_equal(sval24, SIMD[DEST_TYPE, 2](8_388_607, 8_388_606))

    assert_equal(sval24.sub_with_overflow(-1), SIMD[DType.bool, 2](True, False))
    _assert_equal(sval24, SIMD[DEST_TYPE, 2](-8_388_608, 8_388_607))

    assert_equal(uval24.sub_with_overflow(1), SIMD[DType.bool, 2](True, False))
    _assert_equal(uval24, SIMD[DEST_TYPE, 2](16_777_215, 16_777_214))


fn test_comparison() raises:
    @always_inline
    @parameter
    fn _test_cmp[bits: Int, signed: Bool]() raises:
        alias WIDTH = 4

        var val1: BigInt[bits, size=WIDTH, signed=signed]
        var val2: __type_of(val1)

        @parameter
        if signed:
            val1 = SIMD[DEST_TYPE, WIDTH](-1, 0, 1, 2)
            val2 = SIMD[DEST_TYPE, WIDTH](-2, 0, 2, -1)
        else:
            val1 = SIMD[DEST_TYPE, WIDTH](1, 0, 1, 2)
            val2 = SIMD[DEST_TYPE, WIDTH](0, 0, 2, 1)

        assert_equal(
            val1 == val2, SIMD[DType.bool, WIDTH](False, True, False, False)
        )
        assert_equal(
            val1 != val2, SIMD[DType.bool, WIDTH](True, False, True, True)
        )
        assert_equal(
            val1 < val2, SIMD[DType.bool, WIDTH](False, False, True, False)
        )
        assert_equal(
            val1 <= val2, SIMD[DType.bool, WIDTH](False, True, True, False)
        )
        assert_equal(
            val1 > val2, SIMD[DType.bool, WIDTH](True, False, False, True)
        )
        assert_equal(
            val1 >= val2, SIMD[DType.bool, WIDTH](True, True, False, True)
        )

    _test_cmp[8, signed=True]()
    _test_cmp[8, signed=False]()

    _test_cmp[24, signed=True]()
    _test_cmp[24, signed=False]()


fn test_invert() raises:
    _assert_equal(~BigInt[8, size=1](1), -2)
    _assert_equal(~BigInt[24, size=1](1), -2)

    _assert_equal(~BigUInt[8, size=1](1), 254)
    _assert_equal(~BigUInt[24, size=1](1), 16_777_214)

    _assert_equal(~BigInt[8, size=1](127), -128)
    _assert_equal(~BigInt[24, size=1](8_388_607), -8_388_608)

    _assert_equal(~BigUInt[8, size=1](127), 128)
    _assert_equal(~BigUInt[24, size=1](8_388_607), 8_388_608)


fn test_lshift() raises:
    var sval8 = BigInt[8, size=4, word_type = DType.uint8](-99)
    var uval8 = BigUInt[8, size=4, word_type = DType.uint8](157)
    var offset = SIMD[DType.index, 4](0, 1, 4, 7)

    _assert_equal(sval8 << 0, -99)
    _assert_equal(sval8 << offset, SIMD[DEST_TYPE, 4](-99, 58, -48, -128))

    _assert_equal(uval8 << 0, 157)
    _assert_equal(uval8 << offset, SIMD[DEST_TYPE, 4](157, 58, 208, 128))

    var sval24 = BigInt[24, size=4, word_type = DType.uint8](-3_962_546)
    var uval24 = BigUInt[24, size=4, word_type = DType.uint8](12_814_670)
    offset = SIMD[DType.index, 4](0, 1, 12, 23)

    _assert_equal(sval24 << 0, -3_962_546)
    _assert_equal(
        sval24 << offset,
        SIMD[DEST_TYPE, 4](-3_962_546, -7_925_092, -7_020_544, 0),
    )

    _assert_equal(uval24 << 0, 12_814_670)
    _assert_equal(
        uval24 << offset,
        SIMD[DEST_TYPE, 4](12_814_670, 8_852_124, 9_756_672, 0),
    )


fn test_ilshift() raises:
    var val = BigInt[24, size=1](1)
    val <<= 8
    _assert_equal(val, 256)


fn test_rshift() raises:
    var sval8 = BigInt[8, word_type = DType.uint8](
        SIMD[DType.int8, 4](1, 99, -99, -1)
    )
    var uval8 = BigUInt[8, size=4, word_type = DType.uint8](157)
    var offset = SIMD[DType.index, 4](0, 1, 4, 7)

    _assert_equal(sval8 >> 0, SIMD[DEST_TYPE, 4](1, 99, -99, -1))
    _assert_equal(sval8 >> offset, SIMD[DEST_TYPE, 4](1, 49, -7, -1))

    _assert_equal(uval8 >> 0, 157)
    _assert_equal(uval8 >> offset, SIMD[DEST_TYPE, 4](157, 78, 9, 1))

    var sval24 = BigInt[24, size=4, word_type = DType.uint8](-3_962_546)
    var uval24 = BigUInt[24, size=4, word_type = DType.uint8](12_814_670)
    offset = SIMD[DType.index, 4](0, 1, 12, 23)

    _assert_equal(sval24 >> 0, -3_962_546)
    _assert_equal(
        sval24 >> offset,
        SIMD[DEST_TYPE, 4](-3_962_546, -1_981_273, -968, -1),
    )

    _assert_equal(uval24 >> 0, 12_814_670)
    _assert_equal(
        uval24 >> offset,
        SIMD[DEST_TYPE, 4](12_814_670, 6_407_335, 3_128, 1),
    )


fn test_irshift() raises:
    var val = BigInt[24, size=1].min()
    val >>= 16
    _assert_equal(val, -128)


fn test_negation() raises:
    var val = SIMD[DEST_TYPE, 4](-2, -1, 0, 1)

    @always_inline
    @parameter
    fn _test_neg[bits: Int, signed: Bool]() raises:
        _assert_equal(
            -BigInt[bits, signed=signed](val),
            BigInt[bits, signed=signed](-val).cast[DEST_TYPE](),
        )

    _test_neg[8, signed=True]()
    _test_neg[8, signed=False]()

    _test_neg[24, signed=True]()
    _test_neg[24, signed=False]()


fn test_unary_plus() raises:
    var val = SIMD[DEST_TYPE, 4](-2, -1, 0, 1)

    @always_inline
    @parameter
    fn _test_plus[bits: Int, signed: Bool]() raises:
        var original = BigInt[bits, signed=signed](val)
        var updated = +original

        _assert_equal(updated, original.cast[DEST_TYPE]())

        updated += 1
        _assert_equal(updated, (original + 1).cast[DEST_TYPE]())

    _test_plus[8, signed=True]()
    _test_plus[8, signed=False]()

    _test_plus[24, signed=True]()
    _test_plus[24, signed=False]()


fn test_cast_to_signed_big_int() raises:
    var sval8 = BigInt[8, size=1](-1)
    var uval8 = BigUInt[8, size=1](255)

    _assert_equal(sval8.cast[8, signed=True](), -1)
    _assert_equal(sval8.cast[24, signed=True](), -1)
    _assert_equal(uval8.cast[8, signed=True](), -1)
    _assert_equal(uval8.cast[24, signed=True](), 255)

    var sval24 = BigInt[24, size=1](-1)
    var uval24 = BigUInt[24, size=1](16_777_215)

    _assert_equal(sval24.cast[8, signed=True](), -1)
    _assert_equal(sval24.cast[24, signed=True](), -1)
    _assert_equal(uval24.cast[8, signed=True](), -1)
    _assert_equal(uval24.cast[24, signed=True](), -1)


fn test_cast_to_unsigned_big_int() raises:
    var sval8 = BigInt[8, size=1](-1)
    var uval8 = BigUInt[8, size=1](255)

    _assert_equal(sval8.cast[8, signed=False](), 255)
    _assert_equal(sval8.cast[24, signed=False](), 255)
    _assert_equal(uval8.cast[8, signed=False](), 255)
    _assert_equal(uval8.cast[24, signed=False](), 255)

    var sval24 = BigInt[24, size=1](-1)
    var uval24 = BigUInt[24, size=1](16_777_215)

    _assert_equal(sval24.cast[8, signed=False](), 255)
    _assert_equal(sval24.cast[24, signed=False](), 16_777_215)
    _assert_equal(uval24.cast[8, signed=False](), 255)
    _assert_equal(uval24.cast[24, signed=False](), 16_777_215)


fn test_cast_to_simd() raises:
    alias DTYPES = List[DType](
        DType.int8,
        DType.uint8,
        DType.int16,
        DType.uint16,
        DType.int32,
        DType.uint32,
        DType.int64,
        DType.uint64,
    )
    var pos_num = Int64(876925160929511475)
    var neg_num = Int64(-876925160929511475)

    var pos_sval = BigInt[64](pos_num)
    var neg_sval = BigInt[64](neg_num)
    var uval = BigUInt[64](pos_num)

    @parameter
    for i in range(len(DTYPES)):
        alias DTYPE = DTYPES[i]

        var casted_pos_num = pos_num.cast[DTYPE]()
        var casted_neg_num = neg_num.cast[DTYPE]()

        assert_equal(pos_sval.cast[DTYPE](), casted_pos_num)
        assert_equal(neg_sval.cast[DTYPE](), casted_neg_num)
        assert_equal(uval.cast[DTYPE](), casted_pos_num)


fn test_most_significant_digit() raises:
    var sval = BigInt[24, size=1].max()
    var uval = BigUInt[24, size=1].max()

    _assert_equal(sval, 8_388_607)
    _assert_equal(uval, 16_777_215)

    assert_equal(sval.get_most_significant_bit(), False)
    assert_equal(uval.get_most_significant_bit(), True)

    sval.set_most_significant_bit()
    uval.clear_most_significant_bit()

    assert_equal(sval.get_most_significant_bit(), True)
    assert_equal(uval.get_most_significant_bit(), False)

    _assert_equal(sval, -1)
    _assert_equal(uval, 8_388_607)

    sval.clear_most_significant_bit()
    uval.set_most_significant_bit()

    _assert_equal(sval, 8_388_607)
    _assert_equal(uval, 16_777_215)

    assert_equal(sval.get_most_significant_bit(), False)
    assert_equal(uval.get_most_significant_bit(), True)


fn test_count_leading_zeros() raises:
    var uval8 = BigUInt[8, size=1](0)

    assert_equal(uval8.count_leading_zeros(), 8)

    uval8.set_most_significant_bit()
    assert_equal(uval8.count_leading_zeros(), 0)

    uval8 >>= 1
    assert_equal(uval8.count_leading_zeros(), 1)

    var uval24 = BigUInt[24, size=1](0)

    assert_equal(uval24.count_leading_zeros(), 24)

    uval24.set_most_significant_bit()
    assert_equal(uval24.count_leading_zeros(), 0)

    uval24 >>= 12
    assert_equal(uval24.count_leading_zeros(), 12)


fn test_is_negative() raises:
    var val = SIMD[DType.int8, 2](-1, 0)

    assert_equal(BigInt[8](val).is_negative(), val < 0)
    assert_equal(BigUInt[8](val).is_negative(), False)

    assert_equal(BigInt[24](val).is_negative(), val < 0)
    assert_equal(BigUInt[24](val).is_negative(), False)


fn test_is_zero() raises:
    var val = SIMD[DType.int8, 4](-1, 0, 1, 2)
    var expected = val == 0

    assert_equal(BigInt[8](val).is_zero(), expected)
    assert_equal(BigUInt[8](val).is_zero(), expected)

    assert_equal(BigInt[24](val).is_zero(), expected)
    assert_equal(BigUInt[24](val).is_zero(), expected)


fn main() raises:
    test_init()
    test_init_unsafe()
    test_init_from_int_literal()
    test_init_from_int()
    test_init_from_signed_simd()
    test_init_from_unsigned_simd()
    test_explicit_copy()

    test_min()
    test_max()

    test_add()
    test_iadd()
    test_add_with_overflow()

    test_sub()
    test_isub()
    test_sub_with_overflow()

    test_comparison()

    test_invert()

    test_lshift()
    test_ilshift()

    test_rshift()
    test_irshift()

    test_negation()
    test_unary_plus()

    test_cast_to_signed_big_int()
    test_cast_to_unsigned_big_int()
    test_cast_to_simd()

    test_most_significant_digit()

    test_count_leading_zeros()

    test_is_negative()

    test_is_zero()


--- test/utils/test_functional.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
# RUN: %mojo %build_dir %assertion_flag %debug_level %sanitize_checks %s

"""Tests for higher-order functions."""

from collections import List, Optional
from sys.info import simdwidthof
from tensor import Tensor

from specials.utils.functional import elementwise, fori_loop
from test_utils import UnitTest


fn _tensor_mul[
    type: DType
](
    a: Tensor[type],
    b: Tensor[type],
    /,
    *,
    min_simds_per_core: Optional[Int] = None,
) raises -> Tensor[type]:
    alias simd_width = simdwidthof[type]()

    if a.shape() != b.shape():
        raise Error("arguments `a` and `b` must have the same shape")

    if min_simds_per_core.or_else(1) < 1:
        raise Error(
            "argument `min_simds_per_core` must be `None` or a positive"
            " integer value"
        )

    var ret = Tensor[type](a.shape())

    @always_inline
    @parameter
    fn func[simd_width: Int](index: Int):
        var x = a.load[width=simd_width](index)
        var y = b.load[width=simd_width](index)
        ret.store[width=simd_width](index, x * y)

    var num_elements = a.num_elements()

    if min_simds_per_core:
        elementwise[func, simd_width=simd_width](
            num_elements, min_simds_per_core=min_simds_per_core.value()[]
        )
    else:
        elementwise[func, simd_width=simd_width](num_elements)

    return ret^


fn test_elementwise() raises:
    @parameter
    fn test_fn[type: DType]() raises:
        var unit_test = UnitTest("test_elementwise")

        var a = 1.0 + Tensor[type].rand(4096)
        var b = 1.0 + Tensor[type].rand(a.shape())

        var actual = _tensor_mul(a, b)
        var expected = a * b

        unit_test.assert_true(actual == expected, msg=str(type))

    test_fn[DType.float32]()
    test_fn[DType.float64]()


fn test_elementwise_parallel() raises:
    @parameter
    fn test_fn[type: DType](num_elements: Int) raises:
        var unit_test = UnitTest("test_elementwise_parallel")

        var a = 1.0 + Tensor[type].rand(num_elements)
        var b = 1.0 + Tensor[type].rand(a.shape())

        var actual = _tensor_mul(a, b, min_simds_per_core=1)
        var expected = a * b

        unit_test.assert_true(
            actual == expected, msg=str(type) + "_" + str(num_elements)
        )

    for num_elements in List(1, 5, 25, 125, 625):
        test_fn[DType.float32](num_elements[])
        test_fn[DType.float64](num_elements[])


fn test_elementwise_sequential() raises:
    @parameter
    fn test_fn[type: DType](num_elements: Int) raises:
        var unit_test = UnitTest("test_elementwise_sequential")

        var a = 1.0 + Tensor[type].rand(num_elements)
        var b = 1.0 + Tensor[type].rand(a.shape())

        var actual = _tensor_mul(a, b, min_simds_per_core=num_elements + 1)
        var expected = a * b

        unit_test.assert_true(
            actual == expected, msg=str(type) + "_" + str(num_elements)
        )

    for num_elements in List(1, 5, 25, 125, 625):
        test_fn[DType.float32](num_elements[])
        test_fn[DType.float64](num_elements[])


fn test_fori_loop_increasing_by_1() raises:
    var unit_test = UnitTest("test_fori_loop_increasing_by_1")
    var actual = List[Int](capacity=5)
    var expected = List[Int](0, 1, 2, 3, 4)

    @always_inline
    @parameter
    fn func[i: Int]():
        actual.append(i)

    fori_loop[func, 0, 5, 1]()

    unit_test.assert_equal(len(actual), 5)
    unit_test.assert_equal(len(expected), 5)

    for i in range(len(actual)):
        unit_test.assert_equal(actual[i], expected[i])


fn test_fori_loop_decreasing_by_2() raises:
    var unit_test = UnitTest("test_fori_loop_decreasing_by_2")
    var actual = List[Int](capacity=3)
    var expected = List[Int](5, 3, 1)

    @always_inline
    @parameter
    fn func[i: Int]():
        actual.append(i)

    fori_loop[func, 5, -1, -2]()

    unit_test.assert_equal(len(actual), 3)
    unit_test.assert_equal(len(expected), 3)

    for i in range(len(actual)):
        unit_test.assert_equal(actual[i], expected[i])


fn test_fori_loop_with_conditional() raises:
    var unit_test = UnitTest("test_fori_loop_with_conditional")
    var actual = List[Int](capacity=3)
    var expected = List[Int](5, 4, 3)

    @always_inline
    @parameter
    fn func[i: Int]() -> Bool:
        actual.append(i)
        if i <= 3:
            return False
        return True

    fori_loop[func, 5, -1, -1]()

    unit_test.assert_equal(len(actual), 3)
    unit_test.assert_equal(len(expected), 3)

    for i in range(len(actual)):
        unit_test.assert_equal(actual[i], expected[i])


fn main() raises:
    test_elementwise()
    test_elementwise_parallel()
    test_elementwise_sequential()
    test_fori_loop_increasing_by_1()
    test_fori_loop_decreasing_by_2()
    test_fori_loop_with_conditional()


--- test/utils/test_numerics.mojo ---

# ===----------------------------------------------------------------------=== #
# Copyright 2024 The Specials Authors.
#
# 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
#
#     https://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.
# ===----------------------------------------------------------------------=== #
# RUN: %mojo %build_dir %assertion_flag %debug_level %sanitize_checks %s

"""Tests for utilities that work with numeric types."""

import math

from specials.utils.numerics import FloatLimits
from test_utils import UnitTest


fn test_float_limits_digits() raises:
    var unit_test = UnitTest("test_float_limits_digits")

    unit_test.assert_equal(FloatLimits[DType.float16].digits, 11)
    unit_test.assert_equal(FloatLimits[DType.float32].digits, 24)
    unit_test.assert_equal(FloatLimits[DType.float64].digits, 53)


fn test_float_limits_max_exponent() raises:
    var unit_test = UnitTest("test_float_limits_max_exponent")

    unit_test.assert_equal(FloatLimits[DType.float16].max_exponent, 16)
    unit_test.assert_equal(FloatLimits[DType.float32].max_exponent, 128)
    unit_test.assert_equal(FloatLimits[DType.float64].max_exponent, 1024)


fn test_float_limits_min_exponent() raises:
    var unit_test = UnitTest("test_float_limits_min_exponent")

    unit_test.assert_equal(FloatLimits[DType.float16].min_exponent, -13)
    unit_test.assert_equal(FloatLimits[DType.float32].min_exponent, -125)
    unit_test.assert_equal(FloatLimits[DType.float64].min_exponent, -1021)


fn test_float_limits_radix() raises:
    var unit_test = UnitTest("test_float_limits_radix")

    unit_test.assert_equal(FloatLimits[DType.float16].radix, 2)
    unit_test.assert_equal(FloatLimits[DType.float32].radix, 2)
    unit_test.assert_equal(FloatLimits[DType.float64].radix, 2)


fn test_float_limits_denorm_min() raises:
    var unit_test = UnitTest("test_float_limits_denorm_min")

    unit_test.assert_equal(
        FloatLimits[DType.float16].denorm_min(),
        Float64(5.960464477539063e-08),
    )
    unit_test.assert_equal(
        FloatLimits[DType.float32].denorm_min(),
        Float64(1.401298464324817e-45),
    )
    unit_test.assert_equal(
        FloatLimits[DType.float64].denorm_min(),
        math.nextafter(Float64(0.0), 1.0),
    )


fn test_float_limits_epsilon() raises:
    var unit_test = UnitTest("test_float_limits_epsilon")

    unit_test.assert_equal(
        FloatLimits[DType.float16].epsilon(), Float16(0.0009765625)
    )
    unit_test.assert_equal(
        FloatLimits[DType.float32].epsilon(), Float32(1.1920928955078125e-07)
    )
    unit_test.assert_equal(
        FloatLimits[DType.float64].epsilon(), Float64(2.220446049250313e-16)
    )


fn test_float_limits_epsilon_neg() raises:
    var unit_test = UnitTest("test_float_limits_epsilon_neg")

    unit_test.assert_equal(
        FloatLimits[DType.float16].epsilon_neg(), Float16(0.00048828125)
    )
    unit_test.assert_equal(
        FloatLimits[DType.float32].epsilon_neg(), Float32(5.960464477539063e-08)
    )
    unit_test.assert_equal(
        FloatLimits[DType.float64].epsilon_neg(),
        Float64(1.1102230246251565e-16),
    )


fn test_float_limits_lowest() raises:
    var unit_test = UnitTest("test_float_limits_lowest")

    unit_test.assert_equal(
        FloatLimits[DType.float16].lowest(), Float16(-65504.0)
    )
    unit_test.assert_equal(
        FloatLimits[DType.float32].lowest(), Float32(-3.4028234663852886e38)
    )
    unit_test.assert_equal(
        FloatLimits[DType.float64].lowest(), Float64(-1.7976931348623157e308)
    )


fn test_float_limits_max() raises:
    var unit_test = UnitTest("test_float_limits_max")

    unit_test.assert_equal(FloatLimits[DType.float16].max(), Float16(65504.0))
    unit_test.assert_equal(
        FloatLimits[DType.float32].max(), Float32(3.4028234663852886e38)
    )
    unit_test.assert_equal(
        FloatLimits[DType.float64].max(), Float64(1.7976931348623157e308)
    )


fn test_float_limits_min() raises:
    var unit_test = UnitTest("test_float_limits_min")

    unit_test.assert_equal(
        FloatLimits[DType.float16].min(), Float16(6.103515625e-05)
    )
    unit_test.assert_equal(
        FloatLimits[DType.float32].min(), Float32(1.1754943508222875e-38)
    )
    unit_test.assert_equal(
        FloatLimits[DType.float64].min(), Float64(2.2250738585072014e-308)
    )


fn main() raises:
    test_float_limits_digits()
    test_float_limits_max_exponent()
    test_float_limits_min_exponent()
    test_float_limits_radix()
    test_float_limits_denorm_min()
    test_float_limits_epsilon()
    test_float_limits_epsilon_neg()
    test_float_limits_lowest()
    test_float_limits_max()
    test_float_limits_min()



Error processing https://github.com/lrmantovani10/Stable-Diffusion.mojo: Encountered text corresponding to disallowed special token '<|endoftext|>'.
If you want this text to be encoded as a special token, pass it to `allowed_special`, e.g. `allowed_special={'<|endoftext|>', ...}`.
If you want this text to be encoded as normal text, disable the check for this token by passing `disallowed_special=(enc.special_tokens_set - {'<|endoftext|>'})`.
To disable this check for all special tokens, pass `disallowed_special=()`.

Error processing https://github.com/Lynet101/Mojo_community-lib: Invalid URL 'None': No scheme supplied. Perhaps you meant https://None?
Error processing https://github.com/thatstoasty/gojo: 'int' object has no attribute 'get'
--- .github/workflows/python-package.yml ---

# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: Python package

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  build:

    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.11"]

    steps:
    - uses: actions/checkout@v3
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v3
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        python -m pip install flake8 pytest
        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
        pip install -e .
    - name: Lint with flake8
      run: |
        # stop the build if there are Python syntax errors or undefined names
        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
    - name: Test with pytest
      run: |
        pytest


--- .gitignore ---

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
#   For a library or package, you might want to ignore these files since the code is
#   intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# poetry
#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
#   This is especially recommended for binary packages to ensure reproducibility, and is more
#   commonly ignored for libraries.
#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock

# pdm
#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
#   in version control.
#   https://pdm.fming.dev/#use-with-ide
.pdm.toml

# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

# PyCharm
#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
#  and can be added to the global gitignore or merged into this file.  For a more nuclear
#  option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/


--- LICENSE ---

MIT License

Copyright (c) 2023 Manuel Saelices

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


--- README.md ---

# py2mojo

Automated Python to Mojo code translation

A tool to automatically convert Python code to the new [Mojo programming language](https://www.modular.com/mojo).

## Installation

```bash
pip install py2mojo
```

## Usage

You can read the usage by running `py2mojo --help`:

```bash
❯ py2mojo --help
usage: py2mojo [-h] [--inplace] [--extension {mojo,🔥}] [--convert-def-to-fn | --no-convert-def-to-fn] [--convert-class-to-struct | --no-convert-class-to-struct] [--float-precision {32,64}]
               filenames [filenames ...]

positional arguments:
  filenames

options:
  -h, --help            show this help message and exit
  --inplace             Rewrite the file inplace
  --extension {mojo,🔥}  File extension of the generated files
  --convert-def-to-fn, --no-convert-def-to-fn
  --convert-class-to-struct, --no-convert-class-to-struct
  --float-precision {32,64}
```

Examples:

```bash
❯ py2mojo myfile.py
```

```bash
❯ py2mojo mypackage/*.py
```

## ⚠ Disclaimer

Please be aware that the Mojo programming language is still in its nascent stages of development. As with any young language, there might be frequent updates, changes, and unforeseen quirks in its syntax and behavior. There will probably be instances where the conversion might not work and may require manual adjustments.

So, consider this tool as experimental. Please do not trust the generated code and double-check it.

## Implementation details

This uses a similar approach to the [pyupgrade](https://github.com/asottile/pyupgrade) tool, using the AST parser to analyze the Python code and replace some parts of it with the equivalent Mojo code.

As Mojo is a superset of Python, non-replaced logic should be also a valid Mojo code.

## Contributing

### How to install it locally

1. Fork the repository

2. Clone your fork:

```bash
git clone git@github.com:youraccount/py2mojo.git
```

3. Install it locally:
```
cd py2mojo
pip install -e .
```


--- py2mojo/__init__.py ---



--- py2mojo/converters/__init__.py ---

from .assignment import convert_assignment
from .classdef import convert_classdef
from .functiondef import convert_functiondef

__all__ = [
    'convert_assignment',
    'convert_classdef',
    'convert_functiondef',
]


--- py2mojo/converters/assignment.py ---

import ast
from functools import partial
from typing import Callable, Iterable

from tokenize_rt import Offset, Token

from ..helpers import ast_to_offset, get_annotation_type, find_token, find_token_by_name, get_mojo_type
from ..rules import RuleSet


def _replace_assignment(tokens: list[Token], i: int, rules: RuleSet, new_type: str) -> None:
    tokens.insert(i, Token(name='NAME', src='var '))
    ann_idx = find_token(tokens, i, ':')
    type_idx = find_token_by_name(tokens, ann_idx, name='NAME')
    assign_op_idx = find_token(tokens, type_idx, '=') - 1
    newline_idx = find_token_by_name(tokens, type_idx, name='NEWLINE')
    valid_idxs = (idx for idx in (assign_op_idx, newline_idx) if idx >= 0)
    end_type_idx = min(valid_idxs)
    del tokens[type_idx:end_type_idx]
    tokens.insert(type_idx, Token(name='NAME', src=new_type))


def convert_assignment(node: ast.AnnAssign, rules: RuleSet) -> Iterable[tuple[Offset, Callable]]:
    """Convert an assignment to a mojo assignment."""
    curr_type = get_annotation_type(node.annotation)
    new_type = get_mojo_type(curr_type, rules)
    if not new_type:
        return

    yield (
        ast_to_offset(node),
        partial(
            _replace_assignment,
            new_type=new_type,
        ),
    )


--- py2mojo/converters/classdef.py ---

import ast
from functools import partial
from typing import Iterable

from tokenize_rt import Token

from ..exceptions import ParseException
from ..helpers import ast_to_offset, find_token
from ..rules import RuleSet


def _replace_class_keyword(tokens: list, i: int, rules: RuleSet) -> None:
    idx = find_token(tokens, i, 'class')
    tokens[idx] = Token(name='NAME', src='struct')


def convert_classdef(node: ast.FunctionDef, rules: RuleSet) -> Iterable:
    """Converts the annotation of the given function definition."""
    if rules.convert_class_to_struct:
        for child in node.body:
            if isinstance(child, ast.Assign):
                raise ParseException(node, 'Class contains non type annotated attributes.')

        offset = ast_to_offset(node)
        yield (
            offset,
            partial(
                _replace_class_keyword,
            ),
        )


--- py2mojo/converters/functiondef.py ---

import ast
from functools import partial
from typing import Iterable

from tokenize_rt import Token

from ..exceptions import ParseException
from ..helpers import (
    ast_to_offset,
    get_annotation_type,
    find_token,
    find_token_after_offset,
    find_token_by_name,
    get_mojo_type,
)
from ..rules import RuleSet


def _replace_annotation(
    tokens: list, i: int, rules: RuleSet, end_offset: int, new_type: str, ann_offset: int | None = None
) -> None:
    if ann_offset:
        ann_idx = find_token_after_offset(tokens, i, ann_offset)
    else:
        ann_idx = find_token(tokens, i, ':')
    type_idx = find_token_by_name(tokens, ann_idx, name='NAME')
    end_type_idx = find_token_after_offset(tokens, ann_idx, end_offset)
    del tokens[type_idx:end_type_idx]
    tokens.insert(type_idx, Token(name='NAME', src=new_type))


def _replace_def_keyword(tokens: list, i: int, rules: RuleSet) -> None:
    idx = find_token(tokens, i, 'def')
    tokens[idx] = Token(name='NAME', src='fn')


def _add_declaration(tokens: list, i: int, rules: RuleSet, declaration: str) -> None:
    tokens.insert(i, Token(name='NAME', src=declaration))
    tokens.insert(i + 1, Token(name='UNIMPORTANT_WS', src=' '))


def convert_functiondef(node: ast.FunctionDef, rules: RuleSet = 0) -> Iterable:
    """Converts the annotation of the given function definition."""
    if rules.convert_def_to_fn > 0:
        offset = ast_to_offset(node)
        yield (
            offset,
            partial(
                _replace_def_keyword,
            ),
        )

    if not node.args.args:
        return

    for arg in node.args.args:
        if arg.arg == 'self':
            yield (
                ast_to_offset(arg),
                partial(
                    _add_declaration,
                    declaration='inout',
                ),
            )
            continue

        if rules.convert_def_to_fn and not arg.annotation:
            raise ParseException(
                node, 'For converting a "def" function to "fn", the declaration needs to be fully type annotated'
            )
        curr_type = get_annotation_type(arg.annotation)
        new_type = get_mojo_type(curr_type, rules)

        if not new_type:
            continue

        yield (
            ast_to_offset(arg),
            partial(
                _replace_annotation,
                end_offset=arg.end_col_offset,
                new_type=new_type,
            ),
        )

    if node.returns:
        curr_type = get_annotation_type(node.returns)
        new_type = get_mojo_type(curr_type, rules)
        if not new_type:
            return

        offset = ast_to_offset(node.returns)
        yield (
            offset,
            partial(
                _replace_annotation,
                end_offset=node.returns.end_col_offset,
                new_type=new_type,
                ann_offset=offset.utf8_byte_offset,
            ),
        )


--- py2mojo/exceptions.py ---

import ast


class ParseException(Exception):
    def __init__(self, node: ast.AST, msg: str):
        self.node = node
        self.msg = msg


--- py2mojo/helpers.py ---

import ast
import re

import astor
from rich import print
from rich.text import Text
from tokenize_rt import UNIMPORTANT_WS, Offset, Token

from .rules import RuleSet


def ast_to_offset(node: ast.expr | ast.stmt) -> Offset:
    return Offset(node.lineno, node.col_offset)


def find_token(tokens: list[Token], i: int, src: str) -> int:
    """Find the index of the token with the given src."""
    try:
        while tokens[i].src != src:
            i += 1
    except IndexError:
        return -1
    return i


def find_token_by_name(tokens: list[Token], i: int, name: str) -> int:
    """Find the index of the token with the given name."""
    try:
        while tokens[i].name != name:
            i += 1
    except IndexError:
        return -1
    return i


def find_token_after_offset(tokens: list[Token], i: int, offset: int) -> int:
    """Find the index of the token after the given offset."""
    try:
        while tokens[i].utf8_byte_offset < offset:
            i += 1
    except IndexError:
        return -1
    return i


def fixup_dedent_tokens(tokens: list[Token]) -> None:
    # copied from pyupgrade
    """For whatever reason the DEDENT / UNIMPORTANT_WS tokens are misordered
    | if True:
    |     if True:
    |         pass
    |     else:
    |^    ^- DEDENT
    |+----UNIMPORTANT_WS
    """
    for i, token in enumerate(tokens):
        if token.name == UNIMPORTANT_WS and tokens[i + 1].name == 'DEDENT':
            tokens[i], tokens[i + 1] = tokens[i + 1], tokens[i]


def get_node_name(node: ast.AST) -> str:
    """Returns the name of the given node."""
    if isinstance(node, ast.Name):
        return node.id
    if isinstance(node, ast.Constant):
        return node.value
    elif isinstance(node, ast.Attribute):
        return node.attr
    elif isinstance(node, ast.Call):
        return node.func.id if isinstance(node.func, ast.Name) else node.func.attr
    else:
        return 'unknown'


def get_next_node(node: ast.AST) -> ast.AST:
    """Returns the next node of the given node."""
    if isinstance(node, ast.Attribute):
        return node.value
    elif isinstance(node, ast.Call):
        return node.func if isinstance(node.func, ast.Name) else node.func.value
    else:
        raise ValueError(f'Unexpected node type: {type(node)}')


def get_dot_path(node: ast.Attribute):
    """Returns the dot path of the given attribute node."""
    attr_list = []
    while isinstance(node, ast.Attribute) or isinstance(node, ast.Call):
        attr = get_node_name(node)
        node = get_next_node(node)
        attr_list.append(attr)
    attr_list.append(get_node_name(node))
    return reversed(attr_list)


def get_annotation_type(node: ast.AST) -> str:
    """Returns the type of the given annotation node."""
    match node.__class__.__name__:
        case 'Name':
            curr_type = node.id
        case 'Subscript':
            curr_type = f'{node.value.id}[{get_annotation_type(node.slice)}]'
        case _:
            curr_type = ''
    return curr_type


def get_mojo_type(curr_type: str, rules: RuleSet) -> str:
    """Returns the corresponding Mojo type for the given Python type."""
    patterns = [
        (re.compile(r'int'), 'Int'),
        (re.compile(r'float'), f'Float{rules.float_precision}'),
        (re.compile(r'str'), 'String'),
        (re.compile(r'list'), 'List'),
        (re.compile(r'dict'), 'Dict'),
    ]

    prev_type = ''

    while prev_type != curr_type:
        prev_type = curr_type
        for pattern, replacement in patterns:
            curr_type = pattern.sub(replacement, curr_type)

    return curr_type


def highlight_code_at_position(code: str, line: int, column: int, end_column: int) -> Text:
    lines = code.splitlines()
    highlighted = Text()

    for idx, source_line in enumerate(lines):
        if idx + 1 == line:
            # Highlight the specific column in the given line
            highlighted.append(source_line[:column], style='white')
            for i in range(column, min(end_column, len(source_line))):
                highlighted.append(source_line[i], style='bold black on yellow')
            highlighted.append(source_line[end_column + 1 :], style='white')
        else:
            highlighted.append(source_line, style='white')
        highlighted.append('\n')

    return highlighted


def display_error(node: ast.AST, message: str):
    src = astor.to_source(node)

    highlighted_src = highlight_code_at_position(src, 1, node.col_offset, node.end_col_offset)
    print('\n[bold red]Error:[/bold red]', message)
    print('[light_salmon3]Source[/light_salmon3]:\n', highlighted_src)


--- py2mojo/main.py ---

from __future__ import annotations

import argparse
import ast
from collections.abc import Iterable
import os
import sys
import tokenize
from collections import defaultdict
from typing import Callable, Sequence, TypeAlias

from tokenize_rt import Offset, reversed_enumerate, src_to_tokens, tokens_to_src

from .converters import convert_assignment, convert_functiondef, convert_classdef
from .exceptions import ParseException
from .helpers import display_error, fixup_dedent_tokens
from .rules import get_rules, RuleSet


TokenFunc: TypeAlias = Callable[[ast.AST, RuleSet], Iterable[tuple[Offset, Callable]]]


def get_converters(klass: type) -> list[TokenFunc]:
    return {
        ast.ClassDef: [
            convert_classdef,
        ],
        ast.AnnAssign: [
            convert_assignment,
        ],
        ast.FunctionDef: [
            convert_functiondef,
        ],
    }.get(klass, [])


def visit(tree: ast.Module, rules: RuleSet) -> dict[Offset, TokenFunc]:
    nodes = [tree]
    ret = defaultdict(list)
    while nodes:
        node = nodes.pop()

        for converter in get_converters(type(node)):
            for offset, token_func in converter(node, rules):
                ret[offset].append(token_func)

        for name in reversed(node._fields):
            value = getattr(node, name)

            if isinstance(value, ast.AST):
                nodes.append(value)
            elif isinstance(value, list):
                for subvalue in reversed(value):
                    if isinstance(subvalue, ast.AST):
                        nodes.append(subvalue)
    return ret


def convert_to_mojo(source: str, rules: RuleSet) -> str:
    tree = ast.parse(source)

    callbacks = visit(tree, rules)

    if not callbacks:
        return source

    try:
        tokens = src_to_tokens(source)
    except tokenize.TokenError:
        return source

    fixup_dedent_tokens(tokens)

    for i, token in reversed_enumerate(tokens):
        if not token.src:
            continue

        for callback in callbacks.get(token.offset, ()):
            callback(tokens, i, rules)

    return tokens_to_src(tokens)


def main(argv: Sequence[str] | None = None) -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument('filenames', nargs='+')
    parser.add_argument(
        '--inplace',
        help='Rewrite the file inplace',
        action='store_true',
    )
    parser.add_argument(
        '--extension',
        help='File extension of the generated files',
        choices=['mojo', '🔥'],
        default='🔥',
        type=str,
    )
    parser.add_argument(
        '--convert-def-to-fn',
        default=True,
        action=argparse.BooleanOptionalAction,
    )
    parser.add_argument(
        '--convert-class-to-struct',
        default=True,
        action=argparse.BooleanOptionalAction,
    )
    parser.add_argument(
        '--float-precision',
        default=32,
        type=int,
        choices=[32, 64],
    )
    args = parser.parse_args(argv)

    for filename in args.filenames:
        mojo_filename = filename if args.inplace else f'{os.path.splitext(filename)[0]}.{args.extension}'
        with open(filename) as source_file:
            source = source_file.read()

            rules = get_rules(args)

            try:
                annotated_source = convert_to_mojo(source, rules)
            except ParseException as exc:
                display_error(exc.node, exc.msg)
                sys.exit(1)

            if source != annotated_source:
                print(f'Rewriting {filename}' if args.inplace else f'Rewriting {filename} into {mojo_filename}')
                with open(mojo_filename, 'w', encoding='UTF-8', newline='') as out:
                    out.write(annotated_source)
            else:
                print(f'File {filename} unchanged')

    return 0


if __name__ == '__main__':
    sys.exit(main())


--- py2mojo/rules.py ---

import argparse
from dataclasses import dataclass


@dataclass
class RuleSet:
    convert_def_to_fn: bool = False
    convert_class_to_struct: bool = False
    float_precision: int = 64


def get_rules(args: argparse.Namespace) -> RuleSet:
    return RuleSet(
        convert_def_to_fn=args.convert_def_to_fn,
        convert_class_to_struct=args.convert_class_to_struct,
        float_precision=args.float_precision,
    )


--- pyproject.toml ---

[project]
name = "py2mojo"

version = "0.0.2"

description = "Automated Python to Mojo code translation"

readme = "README.md"

requires-python = ">=3.11"

license = {file = "LICENSE"}

keywords = ["mojo", "code", "development", "converter", "translator"]

authors = [
  {name = "Manuel Saelices", email = "msaelices@gmail.com" }
]

classifiers = [
  "Development Status :: 3 - Alpha",
  "Intended Audience :: Developers",
  "Topic :: Software Development :: Build Tools",
  "License :: OSI Approved :: MIT License",

  "Programming Language :: Python :: 3",
  "Programming Language :: Python :: 3.11",
  "Programming Language :: Python :: 3 :: Only",
]

dynamic = ["dependencies"]

[project.optional-dependencies]
dev = ["check-manifest"]
test = ["coverage", "pytest"]

[project.urls]
"Homepage" = "https://github.com/msaelices/py2mojo"
"Bug Reports" = "https://github.com/msaelices/py2mojo/issues"
"Source" = "https://github.com/msaelices/py2mojo"

[project.scripts]
py2mojo = "py2mojo.main:main"

[tool.setuptools.dynamic]
dependencies = {file = ["requirements.txt"]}

[build-system]
requires = ["setuptools>=43.0.0", "wheel"]
build-backend = "setuptools.build_meta"

[tool.black]
revision = "main"
isort = true
skip_string_normalization = true
log_level = "INFO"
line_length = 120


--- requirements.txt ---

tokenize_rt>=5.2.0
rich>=13.5.2
astor>=0.8.1

--- tests/conftest.py ---

import pytest

# We want pytest assert introspection in the helpers
pytest.register_assert_rewrite('helpers')


--- tests/helpers.py ---

from py2mojo.main import convert_to_mojo
from py2mojo.rules import RuleSet


def validate(source: str, expected: str, rules: RuleSet | None = None) -> None:
    rules = rules or RuleSet()
    converted_source = convert_to_mojo(source, rules=rules)
    assert converted_source == expected


--- tests/test_algorithms.py ---

from helpers import validate
from py2mojo.rules import RuleSet


def test_fibonacci():
    validate(
        '''
def fib(n: int) -> int:
    """
    Calculate the nth Fibonacci number.
    """
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)
''',
        '''
def fib(n: Int) -> Int:
    """
    Calculate the nth Fibonacci number.
    """
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)
''',
    )
    validate(
        '''
def fib(n: int) -> int:
    """
    Calculate the nth Fibonacci number.
    """
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)
''',
        '''
fn fib(n: Int) -> Int:
    """
    Calculate the nth Fibonacci number.
    """
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)
''',
        rules=RuleSet(convert_def_to_fn=True),
    )


--- tests/test_assignment.py ---

from helpers import validate
from py2mojo.rules import RuleSet


def test_assignment_with_basic_types():
    validate(
        'x: int = 10',
        'var x: Int = 10',
    )
    validate(
        'x: float = 10.5',
        'var x: Float64 = 10.5',
    )
    validate(
        'x: float = 10.5',
        'var x: Float32 = 10.5',
        rules=RuleSet(float_precision=32),
    )
    validate(
        'x: str = "foo"',
        'var x: String = "foo"',
    )
    validate(
        'x: int',
        'var x: Int',
    )
    validate(
        '"""docstring"""\nx: int',
        '"""docstring"""\nvar x: Int',
    )
    validate(
        'd: dict = {}',
        'var d: Dict = {}',
    )


def test_assignment_with_list_types():
    validate(
        'x: list[int] = []',
        'var x: List[Int] = []',
    )
    validate(
        'x: list[float] = []',
        'var x: List[Float64] = []',
    )
    validate(
        'x: list = []',
        'var x: List = []',
    )


--- tests/test_classdef.py ---

import pytest

from helpers import validate
from py2mojo.exceptions import ParseException
from py2mojo.rules import RuleSet


def test_classdef():
    validate(
        'class Foo(Bar): pass',
        'class Foo(Bar): pass',
    )
    validate(
        'class Foo(Bar): pass',
        'struct Foo(Bar): pass',
        rules=RuleSet(convert_class_to_struct=True),
    )
    validate(
        'class Foo(Bar): n: int = 10',
        'struct Foo(Bar): var n: Int = 10',
        rules=RuleSet(convert_class_to_struct=True),
    )


def test_classdef_non_fully_annotated_classes():
    validate(
        '''class Number: number = 10''',
        '''class Number: number = 10''',
        rules=RuleSet(convert_class_to_struct=False),
    )
    with pytest.raises(ParseException):
        validate(
            '''class Number: number = 10''',
            '''class Number: number = 10''',
            rules=RuleSet(convert_class_to_struct=True),
        )


--- tests/test_functiondef.py ---

import pytest

from helpers import validate
from py2mojo.exceptions import ParseException
from py2mojo.rules import RuleSet


def test_functiondef_with_no_params():
    validate(
        'def main(): print("Hello world!")',
        'def main(): print("Hello world!")',
    )
    validate(
        'def main(): print("Hello world!")',
        'fn main(): print("Hello world!")',
        rules=RuleSet(convert_def_to_fn=True),
    )


@pytest.mark.parametrize(
    'python_type, mojo_type',
    [
        ('int', 'Int'),
        ('float', 'Float64'),
    ],
)
def test_functiondef_with_basic_types(python_type, mojo_type):
    validate(
        f'def add(x: {python_type}, y: {python_type}) -> {python_type}: return x + y',
        f'def add(x: {mojo_type}, y: {mojo_type}) -> {mojo_type}: return x + y',
    )
    validate(
        f'def add(x: {python_type}, y: {python_type}) -> {python_type}: return x + y',
        f'fn add(x: {mojo_type}, y: {mojo_type}) -> {mojo_type}: return x + y',
        rules=RuleSet(convert_def_to_fn=True),
    )


@pytest.mark.parametrize(
    'python_type, mojo_type',
    [
        ('int', 'Int'),
        ('float', 'Float64'),
    ],
)
def test_functiondef_with_list_types(python_type, mojo_type):
    validate(
        f'def flatten(l1: list[list[{python_type}]]) -> list[{python_type}]: ...',
        f'def flatten(l1: List[List[{mojo_type}]]) -> List[{mojo_type}]: ...',
    )
    validate(
        f'def reverse(l: list[{python_type}]) -> list[{python_type}]: return reversed(l)',
        f'def reverse(l: List[{mojo_type}]) -> List[{mojo_type}]: return reversed(l)',
    )
    validate(
        f'def concat(l1: list[{python_type}], l2: list[{python_type}]) -> {python_type}: return l1 + l2',
        f'def concat(l1: List[{mojo_type}], l2: List[{mojo_type}]) -> {mojo_type}: return l1 + l2',
    )
    validate(
        'def concat(l1: list, l2: list) -> list: return l1 + l2',
        'def concat(l1: List, l2: List) -> List: return l1 + l2',  # no changed
    )


def test_functiondef_with_float_in_precision():
    validate(
        'def add(x: float, y: float) -> float: return x + y',
        'fn add(x: Float32, y: Float32) -> Float32: return x + y',
        rules=RuleSet(convert_def_to_fn=True, float_precision=32),
    )


def test_functiondef_inside_classes():
    validate(
        '''
class Point:
    def __init__(self, x: int, y: int) -> int: ...
''',
        '''
class Point:
    def __init__(inout self, x: Int, y: Int) -> Int: ...
''',
    )


def test_functiondef_non_fully_annotated_functions():
    validate(
        '''def add(x, y): return x + y''',
        '''def add(x, y): return x + y''',
    )
    with pytest.raises(ParseException):
        validate(
            '''def add(x, y): return x + y''',
            '''def add(x, y): return x + y''',
            rules=RuleSet(convert_def_to_fn=True),
        )



--- .github/workflows/pre-commit.yml ---

name: Run pre-commit

on:
  push:
    branches:
      - '*'

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v2

    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: 3.9

    - name: Install modular CLI
      env:
        MODULAR_AUTH: ${{ secrets.MODULAR_AUTH }}
      run: |
        curl https://get.modular.com | MODULAR_AUTH="$MODULAR_AUTH" sh -
        modular auth "$MODULAR_AUTH"

    - name: Install mojo CLI
      run: |
        modular install nightly/mojo

    - name: Install pre-commit
      run: |
        pip install pre-commit
        pre-commit install

    - name: Run pre-commit checks
      run: |
        export MODULAR_HOME="/home/runner/.modular"
        export PATH="/home/runner/.modular/pkg/packages.modular.com_nightly_mojo/bin:$PATH"
        pre-commit run --all-files

--- .github/workflows/release-package.yml ---

name: Upload Release Artifacts

on:
  push:
    tags:
      - '*'

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: '3.9'

      - name: Install modular CLI
        env:
          MODULAR_AUTH: ${{ secrets.MODULAR_AUTH }}
        run: |
          curl https://get.modular.com | MODULAR_AUTH="$MODULAR_AUTH" sh -
          modular auth "$MODULAR_AUTH"
      - name: Install mojo CLI
        run: |
          modular install nightly/mojo
    
      - name: Generate package
        run: | 
          export MODULAR_HOME="/home/runner/.modular"
          export PATH="/home/runner/.modular/pkg/packages.modular.com_nightly_mojo/bin:$PATH"
          bash ./dist/make_release.sh
      
      - name: Release
        uses: softprops/action-gh-release@v1
        if: startsWith(github.ref, 'refs/tags/')
        with:
          files: ./dist/stdlib_extensions.mojopkg


--- .github/workflows/run_unit_tests.yml ---

name: Run mojo unit Tests

on:
  push:
    branches:
      - '*'

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v2

    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: 3.9

    - name: Install modular CLI
      env:
        MODULAR_AUTH: ${{ secrets.MODULAR_AUTH }}
      run: |
        curl https://get.modular.com | MODULAR_AUTH="$MODULAR_AUTH" sh -
        modular auth "$MODULAR_AUTH"

    - name: Install mojo CLI
      run: |
        modular install nightly/mojo

    - name: Run tests
      run: |
        export MODULAR_HOME="/home/runner/.modular"
        export PATH="/home/runner/.modular/pkg/packages.modular.com_nightly_mojo/bin:$PATH"
        mojo -D MOJO_ENABLE_ASSERTIONS run_all_tests.mojo
        mojo -D MOJO_ENABLE_ASSERTIONS run_all_tests.mojo

--- .gitignore ---

.vscode/
/stdlib_extensions.mojopkg
/dist/stdlib_extensions.mojopkg


--- .pre-commit-config.yaml ---

repos:
  - repo: local
    hooks:
      - id: mojo-format
        name: mojo-format
        entry: mojo format
        language: system
        files: '\.mojo$'
        stages: [commit]


--- LICENSE ---

MIT License

Copyright (c) 2023 Gabriel de Marmiesse

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


--- README.md ---

# mojo-stdlib-extensions
A replica of python's stdlib in mojo 🔥

**Note**: I am currently working on porting all this code to Mojo's stdlib, thus, this 
repository will soon be obsolete.

```python
from stdlib_extensions.datetime import datetime, timedelta
from stdlib_extensions.builtins.string import rjust
from stdlib_extensions.builtins import list_to_str

def main():
    now = datetime.now()
    print(now.__str__())
    print(now.__repr__())
    print(now.year())
    print(now.month())
    ...

    time_elapsed = datetime.now() - now
    
    print(time_elapsed.total_seconds())

    print(rjust("hello world", 20, "*"))
    
    my_list = List[String]()
    my_list.append("hello")
    my_list.append("world")
    print(list_to_str(my_list))
```

Refer to the python documention for the documentation of those functions.

Dict is not provided, because it's now in Mojo's stdlib

### Complete list of what is available here:

```
stdlib_extensions.os.getpid
stdlib_extensions.os.fspath
stdlib_extensions.os.rmdir
stdlib_extensions.os.unlink
stdlib_extensions.os.urandom

stdlib_extensions.pathlib.Path
-> /, cwd, open, __fspath__, write_text, read_text, unlink, rmdir 

stdlib_extensions.builtins.string.rjust
stdlib_extensions.builtins.string.ljust
stdlib_extensions.builtins.string.endswith
stdlib_extensions.builtins.string.startswith
stdlib_extensions.builtins.string.split
stdlib_extensions.builtins.string.join
stdlib_extensions.builtins.string.replace
stdlib_extensions.builtins.string.removeprefix
stdlib_extensions.builtins.string.removesuffix
stdlib_extensions.builtins.string.expandtabs
stdlib_extensions.builtins.string.rstrip
stdlib_extensions.builtins.string.lstrip
stdlib_extensions.builtins.string.strip

stdlib_extensions.builtins.hex
stdlib_extensions.builtins.to_bytes
stdlib_extensions.builtins.input
stdlib_extensions.builtins.list_to_str
-> for Int and Strings, because Mojo doesn't support multiple traits for the same type yet


stdlib_extensions.builtins.bytes
-> __len__, __str__, __getitem__, __setitem__, ==,
   !=, +, *, +=, *=, fromhex, hex

stdlib_extensions.datetime.datetime
-> microsecond, second, minute, ..., year, +, -, now, min, max

stdlib_extensions.datetime.timedelta
-> total_seconds, total_microseconds, microseconds, seconds, days, +, -, /

stdlib_extensions.datetime.time
-> microsecond, second, minute, hour, min, max

stdlib_extensions.datetime.date
-> year, month, day, min, max, today

stdlib_extensions.time.time_ns()

stdlib_extensions.uuid.uuid4()
stdlib_extensions.uuid.UUID
-> __str__, hex, ==, <, bytes, urn, variant, version
```


### Features missing from mojo to have a perfect replica of the api:
* `__iter__` (we need this badly to avoid indexing errors)
* `@property` decorator
* struct attributes


### Features missing from mojo to improve code readability:
* Union types (we need this badly to avoid code duplication)
* `String.__mul__()`
* f-strings
* subclassing struct
* absolute imports working with `mojo package`

### Contributing:

Any function from the Python stdlib is welcome. Make sure to have the same signatures and apis
(or as close as possible if mojo doesn't support something yet).

Run the tests with `mojo run_all_tests.mojo`.
Reformat with `mojo format ./`.

You can also use the pre-commit hook if you don't want to run manually `mojo format ./` before each commit.

```bash
pip install pre-commit
pre-commit install
```


--- dist/make_release.sh ---

set -ex

mojo package ./stdlib_extensions -o ./dist/stdlib_extensions.mojopkg


--- dist/test_release.sh ---

set -ex

cp ./dist/stdlib_extensions.mojopkg /tmp/stdlib_extensions.mojopkg
cd /tmp

cat <<EOF > /tmp/test_import.mojo
from stdlib_extensions.builtins.string import endswith

def main():
    print(endswith("hello world", "world"))
    print("Success!")
EOF

mojo /tmp/test_import.mojo

mojo build /tmp/test_import.mojo

./test_import


--- run_all_tests.mojo ---

from stdlib_extensions.stdlib_tests.builtins import (
    test_string,
    test_bytes,
    test_hex,
    test_math,
)
from stdlib_extensions.stdlib_tests.datetime import (
    test_utils,
    test_timedelta,
    test_date,
    test_time_class,
    test_timezone,
    test_datetime,
)
from stdlib_extensions.stdlib_tests.pathlib import test_path
from stdlib_extensions.stdlib_tests.os import test_process
from stdlib_extensions import datetime as dt
from stdlib_extensions.stdlib_tests.time import test_time
from stdlib_extensions.stdlib_tests.uuid import test_uuid_class


def run_each_module():
    print("running tests for string")
    test_string.run_tests()
    print("running tests for bytes")
    test_bytes.run_tests()
    print("running tests for hex")
    test_hex.run_tests()
    print("running tests for process")
    test_process.run_tests()
    print("running tests for path")
    test_path.run_tests()
    print("running tests for time")
    test_time.run_tests()
    print("running tests for uuid")
    test_uuid_class.run_tests()
    print("running tests for math")
    test_math.run_tests()
    print("running tests for utils")
    test_utils.run_tests()
    print("running tests for timedelta")
    test_timedelta.run_tests()
    print("running tests for time")
    test_time_class.run_tests()
    print("running tests for date")
    test_date.run_tests()
    print("running tests for timezone")
    test_timezone.run_tests()
    print("running tests for datetime")
    test_datetime.run_tests()


def main():
    test_suite_start_time = dt.datetime.now()
    run_each_module()
    test_suite_end_time = dt.datetime.now()

    print(
        "All tests passed. Tests time: "
        + str(test_suite_end_time - test_suite_start_time)
        + "! 🔥🎉🔥"
    )


--- stdlib_extensions/__init__.mojo ---



--- stdlib_extensions/_utils.mojo ---

fn custom_debug_assert(condition: Bool, message: String):
    if not condition:
        print(message)
        print(
            "Should crash now, if the crash did not happen, you should enable the"
            " assertions with -D MOJO_ENABLE_ASSERTIONS"
        )
        debug_assert(condition, "Custom debug assert failed")


fn custom_debug_assert(message: String):
    print(message)
    print(
        "Should crash now, if the crash did not happen, you should enable the"
        " assertions with -D MOJO_ENABLE_ASSERTIONS"
    )
    debug_assert(False, "Custom debug assert failed")


--- stdlib_extensions/builtins/__init__.mojo ---

from ._generic_list import list_to_str
from ._bytes import bytes, to_bytes
from ..syscalls.filesystem import read_from_stdin
from ._hash import custom_hash
from ._types import Optional
from ._math import divmod, modf
from ._custom_equality import ___eq__


fn input(prompt: String) -> String:
    print(prompt, end="")
    return input()


fn input() -> String:
    return read_from_stdin()[:-1]  # we remove the trailing newline


fn hex(x: UInt8) -> String:
    var hex_table: String = "0123456789abcdef"
    return "0x" + hex_table[(x >> 4).to_int()] + hex_table[(x & 0xF).to_int()]


fn bool_to_int(x: Bool) -> Int:
    """Since int(x) is not available."""
    if x:
        return 1
    else:
        return 0


--- stdlib_extensions/builtins/_bytes.mojo ---

from .._utils import custom_debug_assert
from .string import rjust


fn get_mapping_byte_to_value() -> List[String]:
    var bytes_display = List[String]()
    bytes_display.append("\\x00")
    bytes_display.append("\\x01")
    bytes_display.append("\\x02")
    bytes_display.append("\\x03")
    bytes_display.append("\\x04")
    bytes_display.append("\\x05")
    bytes_display.append("\\x06")
    bytes_display.append("\\x07")
    bytes_display.append("\\x08")
    bytes_display.append("\\t")
    bytes_display.append("\\n")
    bytes_display.append("\\x0b")
    bytes_display.append("\\x0c")
    bytes_display.append("\\r")
    bytes_display.append("\\x0e")
    bytes_display.append("\\x0f")
    bytes_display.append("\\x10")
    bytes_display.append("\\x11")
    bytes_display.append("\\x12")
    bytes_display.append("\\x13")
    bytes_display.append("\\x14")
    bytes_display.append("\\x15")
    bytes_display.append("\\x16")
    bytes_display.append("\\x17")
    bytes_display.append("\\x18")
    bytes_display.append("\\x19")
    bytes_display.append("\\x1a")
    bytes_display.append("\\x1b")
    bytes_display.append("\\x1c")
    bytes_display.append("\\x1d")
    bytes_display.append("\\x1e")
    bytes_display.append("\\x1f")
    bytes_display.append(" ")
    bytes_display.append("!")
    bytes_display.append('"')
    bytes_display.append("#")
    bytes_display.append("$")
    bytes_display.append("%")
    bytes_display.append("&")
    bytes_display.append("'")
    bytes_display.append("(")
    bytes_display.append(")")
    bytes_display.append("*")
    bytes_display.append("+")
    bytes_display.append(",")
    bytes_display.append("-")
    bytes_display.append(".")
    bytes_display.append("/")
    bytes_display.append("0")
    bytes_display.append("1")
    bytes_display.append("2")
    bytes_display.append("3")
    bytes_display.append("4")
    bytes_display.append("5")
    bytes_display.append("6")
    bytes_display.append("7")
    bytes_display.append("8")
    bytes_display.append("9")
    bytes_display.append(":")
    bytes_display.append(";")
    bytes_display.append("<")
    bytes_display.append("=")
    bytes_display.append(">")
    bytes_display.append("?")
    bytes_display.append("@")
    bytes_display.append("A")
    bytes_display.append("B")
    bytes_display.append("C")
    bytes_display.append("D")
    bytes_display.append("E")
    bytes_display.append("F")
    bytes_display.append("G")
    bytes_display.append("H")
    bytes_display.append("I")
    bytes_display.append("J")
    bytes_display.append("K")
    bytes_display.append("L")
    bytes_display.append("M")
    bytes_display.append("N")
    bytes_display.append("O")
    bytes_display.append("P")
    bytes_display.append("Q")
    bytes_display.append("R")
    bytes_display.append("S")
    bytes_display.append("T")
    bytes_display.append("U")
    bytes_display.append("V")
    bytes_display.append("W")
    bytes_display.append("X")
    bytes_display.append("Y")
    bytes_display.append("Z")
    bytes_display.append("[")
    bytes_display.append("\\")
    bytes_display.append("]")
    bytes_display.append("^")
    bytes_display.append("_")
    bytes_display.append("`")
    bytes_display.append("a")
    bytes_display.append("b")
    bytes_display.append("c")
    bytes_display.append("d")
    bytes_display.append("e")
    bytes_display.append("f")
    bytes_display.append("g")
    bytes_display.append("h")
    bytes_display.append("i")
    bytes_display.append("j")
    bytes_display.append("k")
    bytes_display.append("l")
    bytes_display.append("m")
    bytes_display.append("n")
    bytes_display.append("o")
    bytes_display.append("p")
    bytes_display.append("q")
    bytes_display.append("r")
    bytes_display.append("s")
    bytes_display.append("t")
    bytes_display.append("u")
    bytes_display.append("v")
    bytes_display.append("w")
    bytes_display.append("x")
    bytes_display.append("y")
    bytes_display.append("z")
    bytes_display.append("{")
    bytes_display.append("|")
    bytes_display.append("}")
    bytes_display.append("~")
    bytes_display.append("\\x7f")
    bytes_display.append("\\x80")
    bytes_display.append("\\x81")
    bytes_display.append("\\x82")
    bytes_display.append("\\x83")
    bytes_display.append("\\x84")
    bytes_display.append("\\x85")
    bytes_display.append("\\x86")
    bytes_display.append("\\x87")
    bytes_display.append("\\x88")
    bytes_display.append("\\x89")
    bytes_display.append("\\x8a")
    bytes_display.append("\\x8b")
    bytes_display.append("\\x8c")
    bytes_display.append("\\x8d")
    bytes_display.append("\\x8e")
    bytes_display.append("\\x8f")
    bytes_display.append("\\x90")
    bytes_display.append("\\x91")
    bytes_display.append("\\x92")
    bytes_display.append("\\x93")
    bytes_display.append("\\x94")
    bytes_display.append("\\x95")
    bytes_display.append("\\x96")
    bytes_display.append("\\x97")
    bytes_display.append("\\x98")
    bytes_display.append("\\x99")
    bytes_display.append("\\x9a")
    bytes_display.append("\\x9b")
    bytes_display.append("\\x9c")
    bytes_display.append("\\x9d")
    bytes_display.append("\\x9e")
    bytes_display.append("\\x9f")
    bytes_display.append("\\xa0")
    bytes_display.append("\\xa1")
    bytes_display.append("\\xa2")
    bytes_display.append("\\xa3")
    bytes_display.append("\\xa4")
    bytes_display.append("\\xa5")
    bytes_display.append("\\xa6")
    bytes_display.append("\\xa7")
    bytes_display.append("\\xa8")
    bytes_display.append("\\xa9")
    bytes_display.append("\\xaa")
    bytes_display.append("\\xab")
    bytes_display.append("\\xac")
    bytes_display.append("\\xad")
    bytes_display.append("\\xae")
    bytes_display.append("\\xaf")
    bytes_display.append("\\xb0")
    bytes_display.append("\\xb1")
    bytes_display.append("\\xb2")
    bytes_display.append("\\xb3")
    bytes_display.append("\\xb4")
    bytes_display.append("\\xb5")
    bytes_display.append("\\xb6")
    bytes_display.append("\\xb7")
    bytes_display.append("\\xb8")
    bytes_display.append("\\xb9")
    bytes_display.append("\\xba")
    bytes_display.append("\\xbb")
    bytes_display.append("\\xbc")
    bytes_display.append("\\xbd")
    bytes_display.append("\\xbe")
    bytes_display.append("\\xbf")
    bytes_display.append("\\xc0")
    bytes_display.append("\\xc1")
    bytes_display.append("\\xc2")
    bytes_display.append("\\xc3")
    bytes_display.append("\\xc4")
    bytes_display.append("\\xc5")
    bytes_display.append("\\xc6")
    bytes_display.append("\\xc7")
    bytes_display.append("\\xc8")
    bytes_display.append("\\xc9")
    bytes_display.append("\\xca")
    bytes_display.append("\\xcb")
    bytes_display.append("\\xcc")
    bytes_display.append("\\xcd")
    bytes_display.append("\\xce")
    bytes_display.append("\\xcf")
    bytes_display.append("\\xd0")
    bytes_display.append("\\xd1")
    bytes_display.append("\\xd2")
    bytes_display.append("\\xd3")
    bytes_display.append("\\xd4")
    bytes_display.append("\\xd5")
    bytes_display.append("\\xd6")
    bytes_display.append("\\xd7")
    bytes_display.append("\\xd8")
    bytes_display.append("\\xd9")
    bytes_display.append("\\xda")
    bytes_display.append("\\xdb")
    bytes_display.append("\\xdc")
    bytes_display.append("\\xdd")
    bytes_display.append("\\xde")
    bytes_display.append("\\xdf")
    bytes_display.append("\\xe0")
    bytes_display.append("\\xe1")
    bytes_display.append("\\xe2")
    bytes_display.append("\\xe3")
    bytes_display.append("\\xe4")
    bytes_display.append("\\xe5")
    bytes_display.append("\\xe6")
    bytes_display.append("\\xe7")
    bytes_display.append("\\xe8")
    bytes_display.append("\\xe9")
    bytes_display.append("\\xea")
    bytes_display.append("\\xeb")
    bytes_display.append("\\xec")
    bytes_display.append("\\xed")
    bytes_display.append("\\xee")
    bytes_display.append("\\xef")
    bytes_display.append("\\xf0")
    bytes_display.append("\\xf1")
    bytes_display.append("\\xf2")
    bytes_display.append("\\xf3")
    bytes_display.append("\\xf4")
    bytes_display.append("\\xf5")
    bytes_display.append("\\xf6")
    bytes_display.append("\\xf7")
    bytes_display.append("\\xf8")
    bytes_display.append("\\xf9")
    bytes_display.append("\\xfa")
    bytes_display.append("\\xfb")
    bytes_display.append("\\xfc")
    bytes_display.append("\\xfd")
    bytes_display.append("\\xfe")
    bytes_display.append("\\xff")
    return bytes_display


@value
struct bytes(Stringable, Sized, CollectionElement):
    """A mutable sequence of bytes. Behaves like the python version.

    Note that some_bytes[i] returns an UInt8.
    some_bytes *= 2 modifies the sequence in-place. Same with +=.

    Also __setitem__ is available, meaning you can do some_bytes[7] = 105 or
    even some_bytes[7] = some_other_byte (the latter must be only one byte long).
    """

    var _vector: List[UInt8]

    fn __init__(inout self):
        self._vector = List[UInt8]()

    fn __init__(inout self, owned vector: List[UInt8]):
        self._vector = vector^

    fn __init__(inout self, size: Int):
        self._vector = List[UInt8](capacity=size)
        for i in range(size):
            self._vector.append(0)

    @staticmethod
    fn from_values(*values: UInt8) -> bytes:
        var vector = List[UInt8](capacity=len(values))
        for value in values:
            vector.append(value)
        return bytes(vector)

    fn __len__(self) -> Int:
        return len(self._vector)

    fn __getitem__(self, index: Int) -> UInt8:
        return self._vector[index]

    fn __setitem__(inout self, index: Int, value: UInt8):
        self._vector[index] = value

    fn __setitem__(inout self, index: Int, value: bytes):
        self._vector[index] = value[0]

    fn __eq__(self, other: bytes) -> Bool:
        if len(self) != len(other):
            return False
        for i in range(len(self)):
            if self[i] != other[i]:
                return False
        return True

    fn __ne__(self, other: bytes) -> Bool:
        return not (self == other)

    fn __add__(owned self, other: bytes) -> bytes:
        self._vector.extend(other._vector)
        return self

    fn __iadd__(inout self: Self, other: bytes):
        self._vector.extend(other._vector)

    fn __mul__(self, other: Int) -> bytes:
        var new_bytes = bytes()
        for i in range(other):
            new_bytes += self
        return new_bytes

    fn __imul__(inout self: Self, other: Int):
        if other <= 0:
            self._vector.clear()
            return
        var starting_lenght = len(self)
        var iterations = other - 1
        for _ in range(iterations):
            for j in range(starting_lenght):
                self._vector.append(self[j])

    fn __str__(self) -> String:
        alias mapping = get_mapping_byte_to_value()
        var result_string: String = "b'"
        for i in range(len(self)):
            result_string += mapping[self._vector[i].to_int()]
        result_string += "'"
        return result_string

    fn __repr__(self) -> String:
        return self.__str__()

    fn hex(self) -> String:
        var result: String = ""
        for i in range(len(self)):
            var as_hex = hex(self[i])[2:]
            result += rjust(as_hex, 2, "0")
        return result

    fn __hash__(self) -> Int:
        # TODO: do better
        return hash(str(self))

    @staticmethod
    fn fromhex(string: String) -> bytes:
        # TODO: remove whitespaces on the input string
        var vector_of_bytes = List[UInt8](capacity=len(string) // 2)
        var string_length = len(string)
        for i in range(0, string_length, 2):
            var first_char = string[i]
            var second_char = string[i + 1]
            var first_value = _ascii_char_to_int(first_char)
            var second_value = _ascii_char_to_int(second_char)
            var final_value = (first_value << 4) + second_value
            vector_of_bytes.append(UInt8(final_value))
        return bytes(vector_of_bytes)


fn _ascii_char_to_int(char: String) -> Int:
    var ord_value: Int = ord(char)
    if 48 <= ord_value <= 57:
        return ord_value - 48
    elif 65 <= ord_value <= 70:
        return ord_value - 55
    elif 97 <= ord_value <= 102:
        return ord_value - 87
    else:
        custom_debug_assert(False, "Invalid character in hex string")
        return 0


fn to_bytes(n: Int, length: Int = 1, byteorder: String = "big") -> bytes:
    var order = range(0, length, 1)
    if byteorder == "little":
        order = range(0, length, 1)
    elif byteorder == "big":
        order = range(length - 1, -1, -1)
    else:
        custom_debug_assert(False, "byteorder must be either 'little' or 'big'")

    var result_vector = List[UInt8](capacity=length)

    for i in order:
        result_vector.append((n >> i * 8) & 0xFF)

    return bytes(result_vector)


--- stdlib_extensions/builtins/_custom_equality.mojo ---

from ..datetime import time, timedelta

# TODO: move those to __eq__ of the corresponding type when we have conditional traits


fn ___eq__(left: Optional[time], right: Optional[time]) -> Bool:
    if left is None and right is None:
        return True
    if left is not None and right is not None:
        return left.value() == right.value()
    return False


fn ___eq__(left: Optional[timedelta], right: Optional[timedelta]) -> Bool:
    if left is None and right is None:
        return True
    if left is not None and right is not None:
        return left.value() == right.value()
    return False


--- stdlib_extensions/builtins/_generic_list.mojo ---

from .._utils import custom_debug_assert


fn list_to_str(input_list: List[String]) -> String:
    var result: String = "["
    for i in range(len(input_list)):
        var repr = "'" + str(input_list[i]) + "'"
        if i != len(input_list) - 1:
            result += repr + ", "
        else:
            result += repr
    return result + "]"


fn list_to_str(input_list: List[Int]) -> String:
    var result: String = "["
    for i in range(len(input_list)):
        var repr = str(input_list[i])
        if i != len(input_list) - 1:
            result += repr + ", "
        else:
            result += repr
    return result + "]"


fn _cmp_list(a: List[Int], b: List[Int]) -> Int:
    for i in range(len(a)):
        if i >= len(b):
            return 1

        if a[i] < b[i]:
            return -1
        elif a[i] == b[i]:
            continue
        else:
            return 1

    if len(a) < len(b):
        return -1
    else:
        return 0


--- stdlib_extensions/builtins/_hash.mojo ---

from sys.info import sizeof


fn custom_hash(x: List[Int]) -> Int:
    """Very simple hash function."""
    var prime = 31
    var hash_value = 0
    for i in range(len(x)):
        hash_value = prime * hash_value + x[i]
    return hash_value


--- stdlib_extensions/builtins/_math.mojo ---

import math


fn divmod(a: Int, b: Int) -> Tuple[Int, Int]:
    return a // b, a % b


fn divmod(a: Int64, b: Int64) -> Tuple[Int64, Int64]:
    return a // b, a % b


fn modf(x: Float64) -> Tuple[Float64, Float64]:
    var floor = math.trunc(x)
    return (x - floor, floor)


--- stdlib_extensions/builtins/_types.mojo ---

@value
struct Optional[T: CollectionElement](CollectionElement):
    var has_value: Bool
    var values: List[T]

    fn __init__(inout self, value: T):
        self.has_value = True
        self.values = List[T]()
        self.values.append(value)

    fn __init__(inout self, value: None):
        self.has_value = False
        self.values = List[T]()

    fn __is__(self, other: None) -> Bool:
        return not self.has_value

    fn __isnot__(self, other: None) -> Bool:
        return self.has_value

    fn value(self) -> T:
        return self.values[0]


--- stdlib_extensions/builtins/string.mojo ---

from .._utils import custom_debug_assert

alias _ALL_WHITESPACES = " \t\n\r\x0b\f"


fn __string__mul__(input_string: String, n: Int) -> String:
    var result: String = ""
    for _ in range(n):
        result += input_string
    return result


fn rjust(input_string: String, width: Int, fillchar: String = " ") -> String:
    custom_debug_assert(
        len(fillchar) == 1, "The fill character must be exactly one character long"
    )
    var extra = width - len(input_string)
    return __string__mul__(fillchar, extra) + input_string


fn ljust(input_string: String, width: Int, fillchar: String = " ") -> String:
    custom_debug_assert(
        len(fillchar) == 1, "The fill character must be exactly one character long"
    )
    var extra = width - len(input_string)
    return input_string + __string__mul__(fillchar, extra)


fn endswith(
    input_string: String, suffix: String, start: Int = 0, owned end: Int = -1
) -> Bool:
    if end == -1:
        end = len(input_string)

    custom_debug_assert(
        start <= end, "The start index must be less than or equal to the end index"
    )
    if end - start < len(suffix):
        return False

    return input_string[end - len(suffix) : end] == suffix


fn startswith(
    input_string: String, prefix: String, start: Int = 0, owned end: Int = -1
) -> Bool:
    if end == -1:
        end = len(input_string)

    custom_debug_assert(
        start <= end, "The start index must be less than or equal to the end index"
    )

    if end - start < len(prefix):
        return False

    return input_string[start : start + len(prefix)] == prefix


fn string_to_list(input_string: String) -> List[String]:
    var result = List[String]()
    for i in range(len(input_string)):
        result.append(input_string[i])
    return result


fn split(
    input_string: String, sep: String = " ", owned maxsplit: Int = -1
) -> List[String]:
    """The separator can be multiple characters long."""
    var result = List[String]()
    if maxsplit == 0:
        result.append(input_string)
        return result
    if maxsplit < 0:
        maxsplit = len(input_string)

    if not sep:
        return string_to_list(input_string)[0:maxsplit]

    var output = List[String]()
    var start = 0
    var split_count = 0

    for end in range(len(input_string) - len(sep) + 1):
        if input_string[end : end + len(sep)] == sep:
            output.append(input_string[start:end])
            start = end + len(sep)
            split_count += 1

            if maxsplit > 0 and split_count >= maxsplit:
                break

    output.append(input_string[start:])
    return output


fn join(separator: String, iterable: List[String]) -> String:
    var result: String = ""
    for i in range(iterable.__len__()):
        result += iterable[i]
        if i != iterable.__len__() - 1:
            result += separator
    return result


fn replace(input_string: String, old: String, new: String, count: Int = -1) -> String:
    if count == 0:
        return input_string

    var output: String = ""
    var start = 0
    var split_count = 0

    for end in range(len(input_string) - len(old) + 1):
        if input_string[end : end + len(old)] == old:
            output += input_string[start:end] + new
            start = end + len(old)
            split_count += 1

            if count >= 0 and split_count >= count and count >= 0:
                break

    output += input_string[start:]
    return output


fn removeprefix(input_string: String, prefix: String) -> String:
    if startswith(input_string, prefix):
        return input_string[len(prefix) :]
    return input_string


fn removesuffix(input_string: String, suffix: String) -> String:
    if endswith(input_string, suffix):
        return input_string[: -len(suffix)]
    return input_string


fn expandtabs(input_string: String, tabsize: Int = 8) -> String:
    return replace(input_string, "\t", __string__mul__(" ", tabsize))


fn strip(input_string: String, chars: String = _ALL_WHITESPACES) -> String:
    var lstrip_index = _lstrip_index(input_string, chars)
    var rstrip_index = _rstrip_index(input_string, chars)
    return input_string[lstrip_index:rstrip_index]


fn lstrip(input_string: String, chars: String = _ALL_WHITESPACES) -> String:
    return input_string[_lstrip_index(input_string, chars) :]


fn rstrip(input_string: String, chars: String = _ALL_WHITESPACES) -> String:
    return input_string[: _rstrip_index(input_string, chars)]


fn _lstrip_index(input_string: String, chars: String) -> Int:
    for i in range(len(input_string)):
        if input_string[i] not in chars:
            return i
    return len(input_string)


fn _rstrip_index(input_string: String, chars: String) -> Int:
    for i in range(len(input_string) - 1, -1, -1):
        if input_string[i] not in chars:
            return i + 1
    return 0


--- stdlib_extensions/datetime/__init__.mojo ---

from ._date import date
from ._time import time
from ._datetime import datetime
from ._timedelta import timedelta
from ._tzinfo import tzinfo
from ._timezone import timezone


--- stdlib_extensions/datetime/_date.mojo ---

from .. import datetime as dt
from ..builtins import Optional, bytes
from ..builtins import divmod
from ._utils import (
    ord2ymd,
    isoweek1monday,
    ymd2ord,
    _isoweek_to_gregorian,
    _build_struct_time,
    DAYS_NAMES,
    DAYS_SHORT_NAMES,
    MONTHS_SHORT_NAMES,
    MONTHS_NAMES,
    _parse_isoformat_date,
    MAXORDINAL,
    _check_date_fields,
)
from ._iso_calendar_date import IsoCalendarDate

from ..builtins._generic_list import _cmp_list
from ..builtins import custom_hash
from ..time import time, time_ns, struct_time
from ..builtins.string import ljust, rjust, join
from .._utils import custom_debug_assert

alias _EPOCH_DATE = date(1970, 1, 1)


@value
struct date(CollectionElement, Stringable, Hashable):
    """Concrete date type.

    Constructors:

    __new__()
    fromtimestamp()
    today()
    fromordinal()

    Operators:

    __repr__, __str__
    __eq__, __le__, __lt__, __ge__, __gt__, __hash__
    __add__, __radd__, __sub__ (add/radd only with dt.timedelta arg)

    Methods:

    timetuple()
    toordinal()
    weekday()
    isoweekday(), isocalendar(), isoformat()
    ctime()
    strftime()

    Properties (readonly):
    year, month, day
    """

    alias min = date(1, 1, 1)
    alias max = date(9999, 12, 31)
    alias resolution = dt.timedelta(days=1)

    var year: Int
    var month: Int
    var day: Int
    # this is to avoid conflicting with the @value constructor
    # TODO: remove when https://github.com/modularml/mojo/issues/1705 is fixed
    var _dummy: Int

    fn __init__(inout self, year: Int, month: Int, day: Int) -> None:
        # TODO: enable this
        # _check_date_fields(year, month, day)
        self.year = year
        self.month = month
        self.day = day
        self._dummy = 0

    # Additional constructors
    @staticmethod
    def fromtimestamp(t: Int) -> date:
        "Construct a date from a POSIX timestamp (like time.time())."
        return _EPOCH_DATE + dt.timedelta(seconds=t)

    @staticmethod
    def fromtimestamp(t: Float64) -> date:
        "Construct a date from a POSIX timestamp (like time.time())."
        return _EPOCH_DATE + dt.timedelta(seconds=int(t))

    @staticmethod
    fn today() -> date:
        "Construct a date from time.time()."
        var t = time_ns()
        return _EPOCH_DATE + dt.timedelta(microseconds=(t / 1_000).to_int())

    @staticmethod
    fn fromordinal(n: Int) -> date:
        """Construct a date from a proleptic Gregorian ordinal.

        January 1 of year 1 is day 1.  Only the year, month and day are
        non-zero in the result.
        """
        var y: Int
        var m: Int
        var d: Int
        y, m, d = ord2ymd(n)
        return date(y, m, d)

    @staticmethod
    fn fromisoformat(date_string: String) raises -> date:
        """Construct a date from a string in ISO 8601 format."""

        # custom_debug_assert(len(date_string) in (7, 8, 10), "Invalid isoformat string: " + date_string)
        try:
            var year: Int
            var month: Int
            var day: Int
            year, month, day = _parse_isoformat_date(date_string)
            return date(year, month, day)
        except e:
            raise Error("Invalid isoformat string:" + date_string + ", " + e)

    @staticmethod
    fn fromisocalendar(year: Int, week: Int, day: Int) -> date:
        """Construct a date from the ISO year, week number and weekday.

        This is the inverse of the date.isocalendar() function"""
        var gregorian_year: Int
        var gregorian_month: Int
        var gregorian_day: Int
        gregorian_year, gregorian_month, gregorian_day = _isoweek_to_gregorian(
            year, week, day
        )
        return date(gregorian_year, gregorian_month, gregorian_day)

    # Conversions to string
    fn __repr__(self) -> String:
        """Convert to formal string, for repr().

        >>> d = date(2010, 1, 1)
        >>> repr(d)
        'datetime.date(2010, 1, 1)'
        """
        return (
            "datetime.date("
            + str(self.year)
            + ", "
            + str(self.month)
            + ", "
            + str(self.day)
            + ")"
        )

    # XXX These shouldn't depend on time.localtime(), because that
    # clips the usable dates to [1970 .. 2038).  At least ctime() is
    # easily done without using strftime() -- that's better too because
    # strftime("%c", ...) is locale specific.
    fn ctime(self) -> String:
        "Return ctime() style string."
        var weekday = self.toordinal() % 7 or 7
        var standard_week_day = str(DAYS_SHORT_NAMES[weekday])
        var standard_month_day = str(MONTHS_SHORT_NAMES[self.month])
        var space_padded_day = rjust(str(self.day), 2, " ")
        return self.strftime(
            standard_week_day
            + " "
            + standard_month_day
            + " "
            + space_padded_day
            + " 00:00:00 %Y"
        )

    fn strftime(self, owned format: String) -> String:
        """
        Format using strftime().

        Example: "%d/%m/%Y, %H:%M:%S"
        """
        # this letter F is unused
        # if there are performance issues, there might be a way to avoid the replace()
        # but this solution is much easier to understand and read.
        format = format.replace("%:z", "%F")
        var result: String = ""
        var previous_was_percent = False
        for i in range(len(format)):
            var letter = format[i]
            if previous_was_percent:
                result += self._get_from_letter(letter)
                previous_was_percent = False
            elif letter == "%":
                previous_was_percent = True
            else:
                result += letter
        return result

    fn _get_from_letter(self, letter: String) -> String:
        """See https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
        """
        if letter == "%":
            return "%"
        elif letter == "a":
            return DAYS_SHORT_NAMES[self.isoweekday()]
        elif letter == "A":
            return DAYS_NAMES[self.isoweekday()]
        elif letter == "w":
            return str((self.weekday() + 1) % 7)
        elif letter == "d":
            return rjust(str(self.day), 2, "0")
        elif letter == "b":
            return MONTHS_SHORT_NAMES[self.month]
        elif letter == "B":
            return MONTHS_NAMES[self.month]
        elif letter == "m":
            return rjust(str(self.month), 2, "0")
        elif letter == "y":
            return rjust(str(self.year % 100), 2, "0")
        elif letter == "Y":
            return rjust(str(self.year), 4, "0")
        elif letter == "H":
            return "00"
        elif letter == "I":
            return "12"
        elif letter == "p":
            return "AM"
        elif letter == "M":
            return "00"
        elif letter == "S":
            return "00"
        elif letter == "f":
            return "000000"
        elif letter == "z":
            return ""
        elif letter == "Z":
            return ""
        elif letter == "j":
            return rjust((self - date(self.year, 1, 1)).days + 1, 3, "0")
        elif letter == "U":
            custom_debug_assert(False, "Not implemented yet for %U")
            return ""
        elif letter == "W":
            custom_debug_assert(False, "Not implemented yet for %W")
            return ""
        elif letter == "c":
            # the day is padded with a space, weird...
            var day = rjust(str(self.day), 2, " ")
            return self.strftime("%a %b " + day + " %H:%M:%S %Y")
        elif letter == "x":
            return self.strftime("%m/%d/%y")
        elif letter == "X":
            return self.strftime("%H:%M:%S")
        elif letter == "G":
            custom_debug_assert(False, "Not implemented yet for %G")
            return ""
        elif letter == "u":
            return str(self.isoweekday())
        elif letter == "V":
            custom_debug_assert(False, "Not implemented yet for %V")
            return ""
        elif letter == "F":
            return ""
        else:
            custom_debug_assert(
                False, "strftime format string contains unknown format letter"
            )
            return "error in strftime"

    def __format__(self, fmt: String) -> String:
        if len(fmt) != 0:
            return self.strftime(fmt)
        return str(self)

    fn isoformat(self) -> String:
        """Return the date formatted according to ISO.

        This is 'YYYY-MM-DD'.

        References:
        - http://www.w3.org/TR/NOTE-datetime
        - http://www.cl.cam.ac.uk/~mgk25/iso-time.html
        """
        return (
            rjust(str(self.year), 4, "0")
            + "-"
            + rjust(str(self.month), 2, "0")
            + "-"
            + rjust(str(self.day), 2, "0")
        )

    fn __str__(self) -> String:
        """Convert to string, for str().

        >>> d = date(2010, 1, 1)
        >>> str(d)
        '2010-01-01'
        """
        return self.isoformat()

    # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__,
    # __hash__ (and helpers)

    def timetuple(self) -> struct_time:
        "Return local time tuple compatible with time.localtime()."
        return _build_struct_time(self.year, self.month, self.day, 0, 0, 0, -1)

    fn toordinal(self) -> Int:
        """Return proleptic Gregorian ordinal for the year, month and day.

        January 1 of year 1 is day 1.  Only the year, month and day values
        contribute to the result.
        """
        return ymd2ord(self.year, self.month, self.day)

    fn replace(
        self,
        owned year: Optional[Int] = None,
        owned month: Optional[Int] = None,
        owned day: Optional[Int] = None,
    ) -> date:
        """Return a new date with new values for the specified fields."""
        if year is None:
            year = self.year
        if month is None:
            month = self.month
        if day is None:
            day = self.day
        return date(year.value(), month.value(), day.value())

    # Comparisons of date objects with other.
    fn __eq__(self, other: date) -> Bool:
        return self._cmp(other) == 0

    def __le__(self, other: date) -> Bool:
        return self._cmp(other) <= 0

    def __lt__(self, other: date) -> Bool:
        return self._cmp(other) < 0

    fn __ge__(self, other: date) -> Bool:
        return self._cmp(other) >= 0

    fn __gt__(self, other: date) -> Bool:
        return self._cmp(other) > 0

    fn _cmp(self, other: date) -> Int:
        var list_1 = List[Int](self.year, self.month, self.day)
        var list_2 = List[Int](other.year, other.month, other.day)
        return _cmp_list(list_1, list_2)

    fn __hash__(self) -> Int:
        return custom_hash(List[Int](self.year, self.month, self.day))

    # Computations
    fn __add__(self, other: dt.timedelta) -> date:
        "Add a date to a dt.timedelta."
        var o = self.toordinal() + other.days
        custom_debug_assert(0 < o <= MAXORDINAL, "result out of range")
        return date.fromordinal(o)

    fn __sub__(self, other: dt.timedelta) -> date:
        """Subtract two dates, or a date and a dt.timedelta."""
        return self + dt.timedelta(-other.days)

    fn __sub__(self, other: date) -> dt.timedelta:
        var days1 = self.toordinal()
        var days2 = other.toordinal()
        return dt.timedelta(days1 - days2)

    fn weekday(self) -> Int:
        "Return day of the week, where Monday == 0 ... Sunday == 6."
        return (self.toordinal() + 6) % 7

    # Day-of-the-week and week-of-the-year, according to ISO

    fn isoweekday(self) -> Int:
        "Return day of the week, where Monday == 1 ... Sunday == 7."
        # 1-Jan-0001 is a Monday
        return self.toordinal() % 7 or 7

    fn isocalendar(self) -> IsoCalendarDate:
        """Return a named tuple containing ISO year, week number, and weekday.

        The first ISO week of the year is the (Mon-Sun) week
        containing the year's first Thursday; everything else derives
        from that.

        The first week is 1; Monday is 1 ... Sunday is 7.

        ISO calendar algorithm taken from
        http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
        (used with permission)
        """
        var year = self.year
        var week1monday = isoweek1monday(year)
        var today = ymd2ord(self.year, self.month, self.day)
        # Internally, week and day have origin 0
        var week = (today - week1monday) // 7
        var day = (today - week1monday) % 7
        if week < 0:
            year -= 1
            week1monday = isoweek1monday(year)
            week, day = divmod(today - week1monday, 7)
        elif week >= 52:
            if today >= isoweek1monday(year + 1):
                year += 1
                week = 0
        return IsoCalendarDate(year, week + 1, day + 1)

    fn _getstate(self) -> bytes:
        var yhi: Int
        var ylo: Int
        yhi, ylo = divmod(self.year, 256)
        return bytes.from_values(yhi, ylo, self.month, self.day)


--- stdlib_extensions/datetime/_datetime.mojo ---

from .. import datetime as dt
from ._utils import ymd2ord, MAXORDINAL, _check_date_fields, _check_time_fields
from ..builtins import divmod
from ..builtins._types import Optional
from .._utils import custom_debug_assert
from ._utils import (
    _check_utc_offset,
    _check_time_fields,
    _build_struct_time,
)
from ..time import struct_time
from ..builtins.string import join
from utils.variant import Variant
from python import Python
from ..syscalls.clocks import clock_gettime
from ..builtins import custom_hash


# TODO: time methods must be transferred to datetime

alias _EPOCH = datetime(1970, 1, 1, tzinfo=dt.timezone(dt.timedelta(0)))


@value
struct datetime(CollectionElement, Stringable, Hashable):
    #    """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
    #
    #    The year, month and day arguments are required. tzinfo may be None, or an
    #    instance of a tzinfo subclass. The remaining arguments may be ints.
    #    """
    var year: Int
    var month: Int
    var day: Int
    var hour: Int
    var minute: Int
    var second: Int
    var microsecond: Int
    # TODO: use the trait tzinfo instead.
    # traits are too strict right now to do what we want here.
    var tzinfo: Optional[dt.timezone]
    var fold: Int

    # this is to avoid conflicting with the @value constructor
    # TODO: remove when https://github.com/modularml/mojo/issues/1705 is fixed
    var _dummy: Int

    alias min = datetime(1, 1, 1)
    alias max = datetime(9999, 12, 31, 23, 59, 59, 999999)
    alias resolution = dt.timedelta(microseconds=1)

    fn __init__(
        inout self,
        year: Int,
        month: Int,
        day: Int,
        hour: Int = 0,
        minute: Int = 0,
        second: Int = 0,
        microsecond: Int = 0,
        tzinfo: Optional[dt.timezone] = None,
        fold: Int = 0,
    ):
        # _check_date_fields(year, month, day)
        _check_time_fields(hour, minute, second, microsecond, fold)
        self.year = year
        self.month = month
        self.day = day
        self.hour = hour
        self.minute = minute
        self.second = second
        self.microsecond = microsecond
        self.tzinfo = tzinfo
        self.fold = fold
        # TODO: remove when https://github.com/modularml/mojo/issues/1705 is fixed
        self._dummy = 0

    #    @classmethod
    #    def fromtimestamp(cls, timestamp, tz=None):
    #        """Construct a datetime from a POSIX timestamp (like time.time()).
    #
    #        A dt.timezone info object may be passed in as well.
    #        """
    #        return cls._fromtimestamp(timestamp, tz is not None, tz)
    #        var t = timestamp
    #        var utc = tz is not None
    #        frac, t = _math.modf(t)
    #        us = round(frac * 1e6)
    #        if us >= 1000000:
    #            t += 1
    #            us -= 1000000
    #        elif us < 0:
    #            t -= 1
    #            us += 1000000
    #
    #        converter = _time.gmtime if utc else _time.localtime
    #        y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)
    #        ss = min(ss, 59)    # clamp out leap seconds if the platform has them
    #        result = cls(y, m, d, hh, mm, ss, us, tz)
    #        if tz is None and not utc:
    #            # As of version 2015f max fold in IANA database is
    #            # 23 hours at 1969-09-30 13:00:00 in Kwajalein.
    #            # Let's probe 24 hours in the past to detect a transition:
    #            max_fold_seconds = 24 * 3600
    #
    #            # On Windows localtime_s throws an OSError for negative values,
    #            # thus we can't perform fold detection for values of time less
    #            # than the max time fold. See comments in _datetimemodule's
    #            # version of this method for more details.
    #            if t < max_fold_seconds and sys.platform.startswith("win"):
    #                return result
    #
    #            y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]
    #            probe1 = cls(y, m, d, hh, mm, ss, us, tz)
    #            trans = result - probe1 - dt.timedelta(0, max_fold_seconds)
    #            if trans.days < 0:
    #                y, m, d, hh, mm, ss = converter(t + trans // dt.timedelta(0, 1))[:6]
    #                probe2 = cls(y, m, d, hh, mm, ss, us, tz)
    #                if probe2 == result:
    #                    result._fold = 1
    #        elif tz is not None:
    #            result = tz.fromutc(result)
    #        return result

    @staticmethod
    fn now() -> datetime:
        var ctime_spec = clock_gettime()
        return datetime(1970, 1, 1) + dt.timedelta(
            seconds=ctime_spec.tv_sec.to_int(),
            microseconds=(ctime_spec.tv_nsec // 1_000).to_int(),
        )

    fn to_python(self) raises -> PythonObject:
        var python_datetime_module = Python.import_module("datetime")
        # dt.timezone not suppoted yet
        custom_debug_assert(
            self.tzinfo is None,
            "converting to python is not yet support if tzinfo is not None",
        )
        custom_debug_assert(
            self.fold == 0, "converting to python is not yet support if fold is not 0"
        )
        return python_datetime_module.datetime(
            self.year,
            self.month,
            self.day,
            self.hour,
            self.minute,
            self.second,
            self.microsecond,
        )

    @staticmethod
    fn combine(date: date, time: time) -> datetime:
        "Construct a datetime from a given date and a given time."
        return datetime(
            date.year,
            date.month,
            date.day,
            time.hour,
            time.minute,
            time.second,
            time.microsecond,
            time.tzinfo,
            fold=time.fold,
        )

    #    @classmethod
    #    def fromisoformat(cls, date_string):
    #        """Construct a datetime from a string in one of the ISO 8601 formats."""
    #        if not isinstance(date_string, str):
    #            raise TypeError('fromisoformat: argument must be str')
    #
    #        if len(date_string) < 7:
    #            raise ValueError(f'Invalid isoformat string: {date_string!r}')
    #
    #        # Split this at the separator
    #        try:
    #            separator_location = _find_isoformat_datetime_separator(date_string)
    #            dstr = date_string[0:separator_location]
    #            tstr = date_string[(separator_location+1):]
    #
    #            date_components = _parse_isoformat_date(dstr)
    #        except ValueError:
    #            raise ValueError(
    #                f'Invalid isoformat string: {date_string!r}') from None
    #
    #        if tstr:
    #            try:
    #                time_components = _parse_isoformat_time(tstr)
    #            except ValueError:
    #                raise ValueError(
    #                    f'Invalid isoformat string: {date_string!r}') from None
    #        else:
    #            time_components = [0, 0, 0, 0, None]
    #
    #        return cls(*(date_components + time_components))
    #
    fn timetuple(self) -> struct_time:
        "Return local time tuple compatible with time.localtime()."
        var dst = self.dst()
        var dst_as_int: Int
        if dst is None:
            dst_as_int = -1
        elif dst.value() != dt.timedelta(0):
            dst_as_int = 1
        else:
            dst_as_int = 0
        return _build_struct_time(
            self.year,
            self.month,
            self.day,
            self.hour,
            self.minute,
            self.second,
            dst_as_int,
        )

    #
    #    def _mktime(self):
    #        """Return integer POSIX timestamp."""
    #        epoch = datetime(1970, 1, 1)
    #        max_fold_seconds = 24 * 3600
    #        t = (self - epoch) // dt.timedelta(0, 1)
    #        def local(u):
    #            y, m, d, hh, mm, ss = _time.localtime(u)[:6]
    #            return (datetime(y, m, d, hh, mm, ss) - epoch) // dt.timedelta(0, 1)
    #
    #        # Our goal is to solve t = local(u) for u.
    #        a = local(t) - t
    #        u1 = t - a
    #        t1 = local(u1)
    #        if t1 == t:
    #            # We found one solution, but it may not be the one we need.
    #            # Look for an earlier solution (if `fold` is 0), or a
    #            # later one (if `fold` is 1).
    #            u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]
    #            b = local(u2) - u2
    #            if a == b:
    #                return u1
    #        else:
    #            b = t1 - u1
    #            assert a != b
    #        u2 = t - b
    #        t2 = local(u2)
    #        if t2 == t:
    #            return u2
    #        if t1 == t:
    #            return u1
    #        # We have found both offsets a and b, but neither t - a nor t - b is
    #        # a solution.  This means t is in the gap.
    #        return (max, min)[self.fold](u1, u2)
    #
    #
    #    def timestamp(self):
    #        "Return POSIX timestamp as float"
    #        if self._tzinfo is None:
    #            s = self._mktime()
    #            return s + self.microsecond / 1e6
    #        else:
    #            return (self - _EPOCH).total_seconds()
    #
    #    def utctimetuple(self):
    #        "Return UTC time tuple compatible with time.gmtime()."
    #        offset = self.utcoffset()
    #        if offset:
    #            self -= offset
    #        y, m, d = self.year, self.month, self.day
    #        hh, mm, ss = self.hour, self.minute, self.second
    #        return _build_struct_time(y, m, d, hh, mm, ss, 0)
    #
    fn date(self) -> date:
        "Return the date part."
        return date(self.year, self.month, self.day)

    fn time(self) -> time:
        "Return the time part, with tzinfo None."
        return time(
            self.hour, self.minute, self.second, self.microsecond, fold=self.fold
        )

    fn timetz(self) -> time:
        "Return the time part, with same tzinfo."
        return time(
            self.hour,
            self.minute,
            self.second,
            self.microsecond,
            self.tzinfo,
            fold=self.fold,
        )

    fn replace(
        self,
        owned year: Optional[Int] = None,
        owned month: Optional[Int] = None,
        owned day: Optional[Int] = None,
        owned hour: Optional[Int] = None,
        owned minute: Optional[Int] = None,
        owned second: Optional[Int] = None,
        owned microsecond: Optional[Int] = None,
        owned tzinfo: TzinfoReplacement = True,
        owned fold: Optional[Int] = None,
    ) -> datetime:
        """Return a new datetime with new values for the specified fields."""
        if year is None:
            year = self.year
        if month is None:
            month = self.month
        if day is None:
            day = self.day
        if hour is None:
            hour = self.hour
        if minute is None:
            minute = self.minute
        if second is None:
            second = self.second
        if microsecond is None:
            microsecond = self.microsecond
        var _tzinfo: Optional[dt.timezone]
        if tzinfo.is_bool():
            _tzinfo = self.tzinfo
        else:
            _tzinfo = tzinfo.get_tzinfo()
        if fold is None:
            fold = self.fold
        return datetime(
            year.value(),
            month.value(),
            day.value(),
            hour.value(),
            minute.value(),
            second.value(),
            microsecond.value(),
            _tzinfo,
            fold.value(),
        )

    #    __replace__ = replace
    #
    #    def _local_timezone(self):
    #        if self.tzinfo is None:
    #            ts = self._mktime()
    #            # Detect gap
    #            ts2 = self.replace(fold=1-self.fold)._mktime()
    #            if ts2 != ts: # This happens in a gap or a fold
    #                if (ts2 > ts) == self.fold:
    #                    ts = ts2
    #        else:
    #            ts = (self - _EPOCH) // dt.timedelta(seconds=1)
    #        localtm = _time.localtime(ts)
    #        local = datetime(*localtm[:6])
    #        # Extract TZ data
    #        gmtoff = localtm.tm_gmtoff
    #        zone = localtm.tm_zone
    #        return dt.timezone(dt.timedelta(seconds=gmtoff), zone)
    #
    #    def astimezone(self, tz=None):
    #        if tz is None:
    #            tz = self._local_timezone()
    #        elif not isinstance(tz, tzinfo):
    #            raise TypeError("tz argument must be an instance of tzinfo")
    #
    #        mytz = self.tzinfo
    #        if mytz is None:
    #            mytz = self._local_timezone()
    #            myoffset = mytz.utcoffset(self)
    #        else:
    #            myoffset = mytz.utcoffset(self)
    #            if myoffset is None:
    #                mytz = self.replace(tzinfo=None)._local_timezone()
    #                myoffset = mytz.utcoffset(self)
    #
    #        if tz is mytz:
    #            return self
    #
    #        # Convert self to UTC, and attach the new time zone object.
    #        utc = (self - myoffset).replace(tzinfo=tz)
    #
    #        # Convert from UTC to tz's local time.
    #        return tz.fromutc(utc)
    #
    #    # Ways to produce a string.
    #
    #    def ctime(self):
    #        "Return ctime() style string."
    #        weekday = self.toordinal() % 7 or 7
    #        return "%s %s %2d %02d:%02d:%02d %04d" % (
    #            _DAYNAMES[weekday],
    #            _MONTHNAMES[self._month],
    #            self._day,
    #            self._hour, self._minute, self._second,
    #            self._year)
    #

    fn isoformat(self, sep: String = "T", timespec: String = "auto") -> String:
        """Return the time formatted according to ISO.

        The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.
        By default, the fractional part is omitted if self.microsecond == 0.

        If self.tzinfo is not None, the UTC offset is also attached, giving
        giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.

        Optional argument sep specifies the separator between date and
        time, default 'T'.

        The optional argument timespec specifies the number of additional
        terms of the time to include. Valid options are 'auto', 'hours',
        'minutes', 'seconds', 'milliseconds' and 'microseconds'.
        """
        return self.date().isoformat() + sep + self.timetz().isoformat(timespec)

    fn __repr__(self) -> String:
        """Convert to formal string, for repr()."""
        var result: String = "datetime.datetime("
        var components = List[String](
            str(self.year),
            str(self.month),
            str(self.day),
            str(self.hour),
            str(self.minute),
            str(self.second),
            str(self.microsecond),
        )
        for _ in range(2):
            if components[-1] == "0":
                components.pop_back()
        result += join(", ", components)
        if self.tzinfo is not None:
            result += ", tzinfo=" + self.tzinfo.value().__repr__()

        if self.fold:
            result += ", fold=1"
        result += ")"
        return result

    fn __str__(self) -> String:
        "Convert to string, for str()."
        return self.isoformat(sep=" ")

    #    @classmethod
    #    def strptime(cls, date_string, format):
    #        'string, format -> new datetime parsed from a string (like time.strptime()).'
    #        import _strptime
    #        return _strptime._strptime_datetime(cls, date_string, format)

    fn utcoffset(self) -> Optional[dt.timedelta]:
        """Return the timezone offset as dt.timedelta positive east of UTC (negative west of
        UTC)."""
        if self.tzinfo is None:
            return None
        var offset = self.tzinfo.value().utcoffset(self)
        _check_utc_offset("utcoffset", offset)
        return offset

    fn tzname(self) -> Optional[String]:
        """Return the timezone name.

        Note that the name is 100% informational -- there's no requirement that
        it mean anything in particular. For example, "GMT", "UTC", "-500",
        "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
        """
        if self.tzinfo is None:
            return None
        return self.tzinfo.value().tzname(self)

    fn dst(self) -> Optional[dt.timedelta]:
        """Return 0 if DST is not in effect, or the DST offset (as dt.timedelta
        positive eastward) if DST is in effect.

        This is purely informational; the DST offset has already been added to
        the UTC offset returned by utcoffset() if applicable, so there's no
        need to consult dst() unless you're interested in displaying the DST
        info.
        """
        if self.tzinfo is None:
            return None
        var offset = self.tzinfo.value().dst(self)
        _check_utc_offset("dst", offset)
        return offset

    #    # Comparisons of datetime objects with other.
    #
    #    def __eq__(self, other):
    #        if isinstance(other, datetime):
    #            return self._cmp(other, allow_mixed=True) == 0
    #        elif not isinstance(other, date):
    #            return NotImplemented
    #        else:
    #            return False
    #
    #    def __le__(self, other):
    #        if isinstance(other, datetime):
    #            return self._cmp(other) <= 0
    #        elif not isinstance(other, date):
    #            return NotImplemented
    #        else:
    #            _cmperror(self, other)
    #
    #    def __lt__(self, other):
    #        if isinstance(other, datetime):
    #            return self._cmp(other) < 0
    #        elif not isinstance(other, date):
    #            return NotImplemented
    #        else:
    #            _cmperror(self, other)
    #
    #    def __ge__(self, other):
    #        if isinstance(other, datetime):
    #            return self._cmp(other) >= 0
    #        elif not isinstance(other, date):
    #            return NotImplemented
    #        else:
    #            _cmperror(self, other)
    #
    #    def __gt__(self, other):
    #        if isinstance(other, datetime):
    #            return self._cmp(other) > 0
    #        elif not isinstance(other, date):
    #            return NotImplemented
    #        else:
    #            _cmperror(self, other)
    #
    #    def _cmp(self, other, allow_mixed=False):
    #        assert isinstance(other, datetime)
    #        mytz = self._tzinfo
    #        ottz = other._tzinfo
    #        myoff = otoff = None
    #
    #        if mytz is ottz:
    #            base_compare = True
    #        else:
    #            myoff = self.utcoffset()
    #            otoff = other.utcoffset()
    #            # Assume that allow_mixed means that we are called from __eq__
    #            if allow_mixed:
    #                if myoff != self.replace(fold=not self.fold).utcoffset():
    #                    return 2
    #                if otoff != other.replace(fold=not other.fold).utcoffset():
    #                    return 2
    #            base_compare = myoff == otoff
    #
    #        if base_compare:
    #            return _cmp((self._year, self._month, self._day,
    #                         self._hour, self._minute, self._second,
    #                         self._microsecond),
    #                        (other._year, other._month, other._day,
    #                         other._hour, other._minute, other._second,
    #                         other._microsecond))
    #        if myoff is None or otoff is None:
    #            if allow_mixed:
    #                return 2 # arbitrary non-zero value
    #            else:
    #                raise TypeError("cannot compare naive and aware datetimes")
    #        # XXX What follows could be done more efficiently...
    #        diff = self - other     # this will take offsets into account
    #        if diff.days < 0:
    #            return -1
    #        return diff and 1 or 0
    #
    fn __add__(self, other: dt.timedelta) -> datetime:
        "Add a datetime and a dt.timedelta."
        var delta = dt.timedelta(
            self.toordinal(),
            hours=self.hour,
            minutes=self.minute,
            seconds=self.second,
            microseconds=self.microsecond,
        )
        delta = delta + other
        var hour: Int
        var rem: Int
        var minute: Int
        var second: Int
        hour, rem = divmod(delta.seconds, 3600)
        minute, second = divmod(rem, 60)
        custom_debug_assert(0 < delta.days <= MAXORDINAL, "result out of range")
        return datetime.combine(
            date.fromordinal(delta.days),
            time(hour, minute, second, delta.microseconds, tzinfo=self.tzinfo),
        )

    fn toordinal(self) -> Int:
        """Return proleptic Gregorian ordinal for the year, month and day.

        January 1 of year 1 is day 1.  Only the year, month and day values
        contribute to the result.
        """
        return ymd2ord(self.year, self.month, self.day)

    #   mojo doesn't support __radd__ yet
    #    __radd__ = __add__

    fn __sub__(self, other: dt.timedelta) -> datetime:
        return self + (-other)

    fn __sub__(self, other: datetime) -> dt.timedelta:
        var days1 = self.toordinal()
        var days2 = other.toordinal()
        var secs1 = self.second + self.minute * 60 + self.hour * 3600
        var secs2 = other.second + other.minute * 60 + other.hour * 3600
        var base = dt.timedelta(
            days1 - days2, secs1 - secs2, self.microsecond - other.microsecond
        )
        if self.tzinfo is None and other.tzinfo is None:
            return base
        var myoff = self.utcoffset()
        var otoff = other.utcoffset()
        if optional_equal_timedelta(myoff, otoff):
            return base
        if myoff is None or otoff is None:
            custom_debug_assert("cannot mix naive and timezone-aware time")
        return base + otoff.value() - myoff.value()

    fn __hash__(self) -> Int:
        var t: datetime
        if self.fold:
            t = self.replace(fold=0)
        else:
            t = self
        var tzoff = t.utcoffset()
        if tzoff is None:
            return custom_hash(
                List[Int](
                    t.year, t.month, t.day, t.hour, t.minute, t.second, t.microsecond
                )
            )
        else:
            var days = ymd2ord(self.year, self.month, self.day)
            var seconds = self.hour * 3600 + self.minute * 60 + self.second
            return hash(dt.timedelta(days, seconds, self.microsecond) - tzoff.value())


@value
struct TzinfoReplacement:
    var _value: Variant[Optional[dt.timezone], Bool]

    fn __init__(inout self, value: Bool):
        self._value = value

    fn __init__(inout self, value: None):
        self._value = Optional[dt.timezone](value)

    fn __init__(inout self, value: dt.timezone):
        self._value = Optional[dt.timezone](value)

    fn get_tzinfo(self) -> Optional[dt.timezone]:
        return self._value.get[Optional[dt.timezone]]()[]

    fn is_bool(self) -> Bool:
        return self._value.isa[Bool]()


fn optional_equal_timedelta(
    a: Optional[dt.timedelta], b: Optional[dt.timedelta]
) -> Bool:
    # remove this when Optional supports __eq__
    if a is None:
        return b is None
    if b is None:
        return False
    return a.value() == b.value()


--- stdlib_extensions/datetime/_iso_calendar_date.mojo ---

@value
struct IsoCalendarDate:
    var year: Int
    var week: Int
    var weekday: Int

    fn __getitem__(self, index: Int) -> Int:
        if index == 0:
            return self.year
        elif index == 1:
            return self.week
        elif index == 2:
            return self.weekday
        else:
            # raise error here
            return 0

    fn __len__(self) -> Int:
        return 3

    def __repr__(self) -> String:
        return (
            "IsoCalendarDate(year="
            + str(self[0])
            + ", week="
            + str(self[1])
            + ", weekday="
            + str(self[2])
            + ")"
        )


--- stdlib_extensions/datetime/_time.mojo ---

from ..builtins import Optional, ___eq__
from .. import datetime as dt
from utils.variant import Variant
from ..builtins._generic_list import _cmp_list
from ..builtins import custom_hash
from ..builtins import divmod, bool_to_int
from ..builtins.string import rjust, removeprefix
from ._utils import _check_utc_offset, _check_time_fields
from .._utils import custom_debug_assert


@value
struct time(CollectionElement, Hashable, Stringable):
    """Time with time zone.

    Constructors:

    __new__()

    Operators:

    __repr__, __str__
    __eq__, __le__, __lt__, __ge__, __gt__, __hash__

    Methods:

    strftime()
    isoformat()
    utcoffset()
    tzname()
    dst()

    Properties (readonly):
    hour, minute, second, microsecond, tzinfo, fold
    """

    var hour: Int
    var minute: Int
    var second: Int
    var microsecond: Int
    var tzinfo: Optional[timezone]
    var _hashcode: Int
    var fold: Int

    alias min = time(0, 0, 0)
    alias max = time(23, 59, 59, 999999)
    alias resolution = timedelta(microseconds=1)

    fn __init__(
        inout self,
        hour: Int = 0,
        minute: Int = 0,
        second: Int = 0,
        microsecond: Int = 0,
        tzinfo: Optional[timezone] = None,
        fold: Int = 0,
    ):
        """Constructor.

        Arguments:

        hour, minute (required)
        second, microsecond (default to zero)
        tzinfo (default to None)
        fold (keyword only, default to zero)
        """
        _check_time_fields(hour, minute, second, microsecond, fold)
        self.hour = hour
        self.minute = minute
        self.second = second
        self.microsecond = microsecond
        self.tzinfo = tzinfo
        self._hashcode = -1
        self.fold = fold

    # Standard conversions, __hash__ (and helpers)
    # Comparisons of time objects with other.

    fn __eq__(self, other: time) -> Bool:
        return self._cmp(other, allow_mixed=True) == 0

    fn __ne__(self, other: time) -> Bool:
        return self._cmp(other, allow_mixed=True) != 0

    def __le__(self, other: time) -> Bool:
        return self._cmp(other) <= 0

    def __lt__(self, other: time) -> Bool:
        return self._cmp(other) < 0

    def __ge__(self, other: time) -> Bool:
        return self._cmp(other) >= 0

    def __gt__(self, other: time) -> Bool:
        return self._cmp(other) > 0

    fn _cmp(self, other: time, allow_mixed: Bool = False) -> Int:
        var mytz = self.tzinfo
        var ottz = other.tzinfo
        var myoff: Optional[timedelta] = None
        var otoff: Optional[timedelta] = None
        var base_compare: Bool
        if mytz is None and ottz is None:
            base_compare = True
        elif mytz is not None and ottz is not None and mytz.value() == ottz.value():
            base_compare = True
        else:
            myoff = self.utcoffset()
            otoff = other.utcoffset()
            base_compare = ___eq__(myoff, otoff)

        if base_compare:
            return _cmp_list(
                List[Int](self.hour, self.minute, self.second, self.microsecond),
                List[Int](other.hour, other.minute, other.second, other.microsecond),
            )
        if myoff is None or otoff is None:
            if allow_mixed:
                return 2  # arbitrary non-zero value
            else:
                custom_debug_assert("cannot compare naive and aware times")

        # is there a bug here? Does that mean that we cannot have a tzinfo with sub-minute resolution?
        # Anyway, this was in the CPython code, so I'm keeping it for now until someone has the answer.
        var myhhmm = self.hour * 60 + self.minute - myoff.value() // timedelta(
            minutes=1
        )
        var othhmm = other.hour * 60 + other.minute - otoff.value() // timedelta(
            minutes=1
        )
        return _cmp_list(
            List[Int](myhhmm.to_int(), self.second, self.microsecond),
            List[Int](othhmm.to_int(), other.second, other.microsecond),
        )

    fn __hash__(self) -> Int:
        """Hash."""
        var t: time
        if self.fold:
            t = self.replace(fold=0)
        else:
            t = self
        var tzoff = t.utcoffset()
        if tzoff is None or tzoff.value() == timedelta(0):
            return custom_hash(List[Int](t.hour, t.minute, t.second, t.microsecond))
        else:
            var utctime = timedelta(
                hours=self.hour, minutes=self.minute
            ) - tzoff.value()
            var h = utctime // timedelta(hours=1)
            var m = utctime % timedelta(hours=1)
            # assert not m % timedelta(minutes=1), "whole minute"
            var minutes_int = (m // timedelta(minutes=1)).to_int()
            var h_int = h.to_int()
            return custom_hash(
                List[Int](h_int, minutes_int, self.second, self.microsecond)
            )

    # Conversion to string

    fn _tzstr(self, sep: String = ":") -> String:
        """Return formatted timezone offset (+xx:xx) or an empty string."""
        return _format_optional_offset(self.utcoffset(), sep=sep)

    fn __repr__(self) -> String:
        """Convert to formal string, for repr()."""
        var result = "datetime.time(" + str(self.hour) + ", " + str(self.minute)
        if self.second != 0 or self.microsecond != 0:
            result += ", " + str(self.second)
        if self.microsecond != 0:
            result += ", " + str(self.microsecond)
        if self.tzinfo is not None:
            result += ", tzinfo=" + self.tzinfo.value().__repr__()
        if self.fold:
            result += ", fold=1"
        return result + ")"

    fn isoformat(self, timespec: String = "auto") -> String:
        """Return the time formatted according to ISO.

        The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional
        part is omitted if self.microsecond == 0.

        The optional argument timespec specifies the number of additional
        terms of the time to include. Valid options are 'auto', 'hours',
        'minutes', 'seconds', 'milliseconds' and 'microseconds'.
        """
        var s = _format_time(
            self.hour, self.minute, self.second, self.microsecond, timespec
        )
        var tz = self._tzstr()
        if tz:
            s += tz
        return s

    fn __str__(self) -> String:
        return self.isoformat()

    @staticmethod
    fn fromisoformat(owned time_string: String) raises -> time:
        """Construct a time from a string in one of the ISO 8601 formats."""

        # The spec actually requires that time-only ISO 8601 strings start with
        # T, but the extended format allows this to be omitted as long as there
        # is no ambiguity with date strings.
        time_string = removeprefix(time_string, "T")
        var r = _parse_isoformat_time(time_string)
        return time(r.hour, r.minute, r.second, r.microsecond, r.tzinfo)

    fn strftime(self, owned format: String) -> String:
        """
        Format using strftime().

        Example: "%d/%m/%Y, %H:%M:%S"
        """
        # this letter F is unused
        # if there are performance issues, there might be a way to avoid the replace()
        # but this solution is much easier to understand and read.
        format = format.replace("%:z", "%F")
        var result: String = ""
        var previous_was_percent = False
        for i in range(len(format)):
            var letter = format[i]
            if previous_was_percent:
                result += self._get_from_letter(letter)
                previous_was_percent = False
            elif letter == "%":
                previous_was_percent = True
            else:
                result += letter
        return result

    fn _get_from_letter(self, letter: String) -> String:
        """See https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
        """
        if letter == "%":
            return "%"
        elif letter == "a":
            return "Mon"
        elif letter == "A":
            return "Monday"
        elif letter == "w":
            return "1"
        elif letter == "d":
            return "01"
        elif letter == "b":
            return "Jan"
        elif letter == "B":
            return "January"
        elif letter == "m":
            return "01"
        elif letter == "y":
            return "00"
        elif letter == "Y":
            return "1900"
        elif letter == "H":
            return rjust(str(self.hour), 2, "0")
        elif letter == "I":
            var modulo = self.hour % 12
            if modulo == 0:
                return "12"
            else:
                return rjust(str(modulo), 2, "0")
        elif letter == "p":
            if self.hour < 12:
                return "AM"
            else:
                return "PM"
        elif letter == "M":
            return rjust(str(self.minute), 2, "0")
        elif letter == "S":
            return rjust(str(self.second), 2, "0")
        elif letter == "f":
            return rjust(str(self.microsecond), 6, "0")
        elif letter == "z":
            return self._tzstr(sep="")
        elif letter == "Z":
            if self.tzinfo is None:
                return ""
            return self.tzinfo.value().tzname(None)
        elif letter == "j":
            return "001"
        elif letter == "U":
            return "00"
        elif letter == "W":
            return "01"
        elif letter == "c":
            return self.strftime("Mon Jan  1 %H:%M:%S 1900")
        elif letter == "x":
            return "01/01/00"
        elif letter == "X":
            return self.strftime("%H:%M:%S")
        elif letter == "G":
            return "1900"
        elif letter == "u":
            return "1"
        elif letter == "V":
            return "01"
        elif letter == "F":
            return self._tzstr(sep=":")
        else:
            custom_debug_assert(
                False, "strftime format string contains unknown format letter"
            )
            return ""

    def __format__(self, fmt: String) -> String:
        if len(fmt) != 0:
            return self.strftime(fmt)
        return str(self)

    # Timezone functions
    fn utcoffset(self) -> Optional[timedelta]:
        """Return the timezone offset as timedelta, positive east of UTC
        (negative west of UTC)."""
        if self.tzinfo is None:
            return None
        var offset = self.tzinfo.value().utcoffset(None)
        _check_utc_offset("utcoffset", offset)
        return offset

    fn tzname(self) -> Optional[String]:
        """Return the timezone name.

        Note that the name is 100% informational -- there's no requirement that
        it mean anything in particular. For example, "GMT", "UTC", "-500",
        "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
        """
        if self.tzinfo is None:
            return None
        return self.tzinfo.value().tzname(None)

    fn dst(self) -> Optional[timedelta]:
        """Return 0 if DST is not in effect, or the DST offset (as timedelta
        positive eastward) if DST is in effect.

        This is purely informational; the DST offset has already been added to
        the UTC offset returned by utcoffset() if applicable, so there's no
        need to consult dst() unless you're interested in displaying the DST
        info.
        """
        if self.tzinfo is None:
            return None
        var offset = self.tzinfo.value().dst(None)
        _check_utc_offset("dst", offset)
        return offset

    fn replace(
        self,
        owned hour: Optional[Int] = None,
        owned minute: Optional[Int] = None,
        owned second: Optional[Int] = None,
        owned microsecond: Optional[Int] = None,
        owned tzinfo: Variant[Optional[timezone], Bool] = Variant[
            Optional[timezone], Bool
        ](True),
        owned fold: Optional[Int] = None,
    ) -> time:
        """Return a new time with new values for the specified fields."""
        if hour is None:
            hour = self.hour
        if minute is None:
            minute = self.minute
        if second is None:
            second = self.second
        if microsecond is None:
            microsecond = self.microsecond
        if tzinfo.isa[Bool]() and tzinfo.get[Bool]()[] == True:
            tzinfo = self.tzinfo
        if fold is None:
            fold = self.fold
        return time(
            hour=hour.value(),
            minute=minute.value(),
            second=second.value(),
            microsecond=microsecond.value(),
            tzinfo=tzinfo.get[Optional[timezone]]()[],
            fold=fold.value(),
        )


fn _format_optional_offset(off: Optional[timedelta], sep: String) -> String:
    if off is None:
        return ""
    else:
        return _format_offset(off.value(), sep)


fn _format_offset(owned off: timedelta, sep: String) -> String:
    var s: String = ""
    var sign: String
    if off.days < 0:
        sign = "-"
        off = -off
    else:
        sign = "+"
    var hh = off // timedelta(hours=1)
    var mm = off % timedelta(hours=1)
    var mm_int = (mm // timedelta(minutes=1)).to_int()
    var ss = mm % timedelta(minutes=1)
    s += sign + rjust(str(hh), 2, "0") + sep + rjust(str(mm_int), 2, "0")
    if ss or ss.microseconds:
        s += sep + rjust(str(ss.seconds), 2, "0")
        if ss.microseconds:
            s += "." + rjust(str(ss.microseconds), 6, "0")
    return s


fn _format_time(
    hh: Int, mm: Int, ss: Int, owned us: Int, owned timespec: String = "auto"
) -> String:
    if timespec == "auto":
        # Skip trailing microseconds when us==0.
        timespec = "microseconds" if us else "seconds"

    if timespec == "hours":
        return format_hours(hh)
    elif timespec == "minutes":
        return format_minutes(hh, mm)
    elif timespec == "seconds":
        return format_seconds(hh, mm, ss)
    elif timespec == "milliseconds":
        return format_milliseconds(hh, mm, ss, us // 1000)
    elif timespec == "microseconds":
        return format_microseconds(hh, mm, ss, us)
    else:
        custom_debug_assert("Unknown timespec value")
        return "Wrong timespec value in _format_time()"


fn format_hours(hours: Int) -> String:
    return rjust(str(hours), 2, "0")


fn format_minutes(hours: Int, minutes: Int) -> String:
    return format_hours(hours) + ":" + rjust(str(minutes), 2, "0")


fn format_seconds(hours: Int, minutes: Int, seconds: Int) -> String:
    return format_minutes(hours, minutes) + ":" + rjust(str(seconds), 2, "0")


fn format_milliseconds(
    hours: Int, minutes: Int, seconds: Int, milliseconds: Int
) -> String:
    return (
        format_seconds(hours, minutes, seconds) + "." + rjust(str(milliseconds), 3, "0")
    )


fn format_microseconds(
    hours: Int, minutes: Int, seconds: Int, microseconds: Int
) -> String:
    return (
        format_seconds(hours, minutes, seconds) + "." + rjust(str(microseconds), 6, "0")
    )


# TODO: use Tuple when https://github.com/modularml/mojo/issues/1817 is fixed
@value
struct IsoformatTimeResult:
    var hour: Int
    var minute: Int
    var second: Int
    var microsecond: Int
    var tzinfo: Optional[timezone]


fn _parse_isoformat_time(
    tstr: String,
) raises -> IsoformatTimeResult:
    # Format supported is HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]
    var len_str = len(tstr)
    if len_str < 2:
        raise Error("Isoformat time too short")
    # This is equivalent to re.search('[+-Z]', tstr), but faster
    var tz_pos = (tstr.find("-") + 1 or tstr.find("+") + 1 or tstr.find("Z") + 1)
    var timestr = tstr[: tz_pos - 1] if tz_pos > 0 else tstr
    var time_components = _parse_hh_mm_ss_ff(timestr)
    var tzi: Optional[timezone] = None
    # TODO: simplify to -1 when https://github.com/modularml/mojo/issues/1760 is fixed
    if tz_pos == len_str and tstr[len(tstr) - 1] == "Z":
        tzi = timezone(timedelta(0))
    elif tz_pos > 0:
        var tzstr = get_slice_checked(tstr, tz_pos)
        # Valid time zone strings are:
        # HH                  len: 2
        # HHMM                len: 4
        # HH:MM               len: 5
        # HHMMSS              len: 6
        # HHMMSS.f+           len: 7+
        # HH:MM:SS            len: 8
        # HH:MM:SS.f+         len: 10+
        if len(tzstr) == 0 or len(tzstr) == 1 or len(tzstr) == 3:
            raise Error("Malformed time zone string")
        var tz_comps = _parse_hh_mm_ss_ff(tzstr)
        if is_all_zeros(tz_comps):
            tzi = timezone(timedelta(0))
        else:
            var tzsign = -1 if tstr[tz_pos - 1] == "-" else 1
            var td = timedelta(
                hours=tz_comps[0],
                minutes=tz_comps[1],
                seconds=tz_comps[2],
                microseconds=tz_comps[3],
            )
            tzi = timezone(td * tzsign)
    return IsoformatTimeResult(
        time_components[0],
        time_components[1],
        time_components[2],
        time_components[3],
        tzi,
    )


# TODO: remove this when https://github.com/modularml/mojo/issues/1760 is fixed
fn get_slice_checked(string: String, owned start: Int, owned end: Int) -> String:
    if start >= len(string):
        return ""
    return string[start:end]


fn get_slice_checked(string: String, owned start: Int) -> String:
    if start >= len(string):
        return ""
    return string[start:]


fn _parse_hh_mm_ss_ff(tstr: String) raises -> List[Int]:
    # Parses things of the form HH[:?MM[:?SS[{.,}fff[fff]]]]
    var time_comps = List[Int](0, 0, 0, 0)
    var pos = 0
    var has_sep: Bool = False
    var next_char: String
    for comp in range(0, 3):
        if (len(tstr) - pos) < 2:
            raise Error("Incomplete time component")

        time_comps[comp] = atol(tstr[pos : pos + 2])

        pos += 2
        next_char = get_slice_checked(tstr, pos, pos + 1)

        if comp == 0:
            has_sep = next_char == ":"

        if not next_char or comp >= 2:
            break

        if has_sep and next_char != ":":
            raise Error("Invalid time separator: " + next_char)

        pos += bool_to_int(has_sep)

    if pos < len(tstr):
        if not (tstr[pos] == "." or tstr[pos] == ","):
            raise Error("Invalid microsecond component")
        else:
            pos += 1

            var len_remainder = len(tstr) - pos
            var to_parse: Int
            if len_remainder >= 6:
                to_parse = 6
            else:
                to_parse = len_remainder

            time_comps[3] = atol(tstr[pos : (pos + to_parse)])
            if to_parse < 6:
                time_comps[3] *= List[Int](100000, 10000, 1000, 100, 10)[to_parse - 1]
            if len_remainder > to_parse and not all(
                map_input_str(_is_ascii_digit, tstr[(pos + to_parse) :])
            ):
                raise Error("Non-digit values in unparsed fraction")

    return time_comps


fn all(iterable: List[Bool]) -> Bool:
    for element in iterable:
        if not element[]:
            return False
    return True


fn map_input_str(funct: fn (String) -> Bool, s: String) -> List[Bool]:
    var result = List[Bool]()
    for i in range(len(s)):
        result.append(funct(s[i]))
    return result


fn _is_ascii_digit(c: String) -> Bool:
    alias my_str: String = "0123456789"
    for i in range(len(my_str)):
        if c == my_str[i]:
            return True
    return False


fn is_all_zeros(input_list: List[Int]) -> Bool:
    for i in range(len(input_list)):
        if input_list[i] != 0:
            return False
    return True


--- stdlib_extensions/datetime/_timedelta.mojo ---

from ..builtins import divmod, modf
from ..builtins.string import rjust, join
from ..builtins._generic_list import _cmp_list
from ..builtins import custom_hash
from utils.variant import Variant
from math import abs, round
from .._utils import custom_debug_assert


struct timedelta(CollectionElement, Stringable, Hashable):
    """Represent the difference between two datetime objects.

    Supported operators:

    - add, subtract timedelta
    - unary plus, minus, abs
    - compare to timedelta
    - multiply, divide by int

    In addition, datetime supports subtraction of two datetime objects
    returning a timedelta, and addition or subtraction of a datetime
    and a timedelta giving a datetime.

    Representation: (days, seconds, microseconds).
    """

    # The representation of (days, seconds, microseconds) was chosen
    # arbitrarily; the exact rationale originally specified in the docstring
    # was "Because I felt like it."

    var days: Int
    var seconds: Int
    var microseconds: Int

    alias min = timedelta(-999999999)
    alias max = timedelta(
        days=999999999, hours=23, minutes=59, seconds=59, microseconds=999999
    )
    alias resolution = timedelta(microseconds=1)

    fn __init__(
        inout self,
        owned days: Float64 = 0,
        owned seconds: Float64 = 0,
        owned microseconds: Float64 = 0,
        owned milliseconds: Float64 = 0,
        owned minutes: Float64 = 0,
        owned hours: Float64 = 0,
        owned weeks: Float64 = 0,
        *,
        use_floats: Bool,
    ):
        """This is the datetime constructor which can be used to create it from floating point values.

        Since Mojo can't decide which constructor to take when there are unspecified arguments, we have
        to use the keyword arguments `use_floats=True` here to differentiate the two constructors.
        """
        # make sure weeks is a round number:
        var weeks_remainder: Float64
        weeks_remainder, weeks = modf(weeks)
        days += weeks_remainder * 7

        # make sure days is a round number:
        var days_remainder: Float64
        days_remainder, days = modf(days)
        hours += days_remainder * 24

        # make sure hours is a round number:
        var hours_remainder: Float64
        hours_remainder, hours = modf(hours)
        minutes += hours_remainder * 60

        # make sure minutes is a round number:
        var minutes_remainder: Float64
        minutes_remainder, minutes = modf(minutes)
        seconds += minutes_remainder * 60

        # make sure seconds is a round number:
        var seconds_remainder: Float64
        seconds_remainder, seconds = modf(seconds)
        milliseconds += seconds_remainder * 1000

        # make sure milliseconds is a round number:
        var milliseconds_remainder: Float64
        milliseconds_remainder, milliseconds = modf(milliseconds)
        microseconds += milliseconds_remainder * 1000

        # make sure microseconds is a round number:
        microseconds = round(microseconds)

        self.__init__(
            weeks=weeks.to_int(),
            days=days.to_int(),
            hours=hours.to_int(),
            minutes=minutes.to_int(),
            seconds=seconds.to_int(),
            milliseconds=milliseconds.to_int(),
            microseconds=microseconds.to_int(),
        )

    fn __init__(
        inout self,
        owned days: Int = 0,
        owned seconds: Int = 0,
        owned microseconds: Int = 0,
        milliseconds: Int = 0,
        minutes: Int = 0,
        hours: Int = 0,
        weeks: Int = 0,
    ):
        """Creates a datetime struct from integer values.

        Each of the values provided can be positive or negative.

        Args:
            days: The number of days.
            seconds: The number of seconds.
            microseconds: The number of microseconds.
            milliseconds: The number of milliseconds.
            minutes: The number of minutes.
            hours: The number of hours.
            weeks: The number of weeks.

        """
        # We keep only days, seconds, microseconds
        microseconds += milliseconds * 1000
        seconds += minutes * 60 + hours * 3600
        days += weeks * 7
        self.__init__(days, seconds, microseconds, is_normalized=False)

    fn __init__(
        inout self,
        owned days: Int,
        owned seconds: Int,
        owned microseconds: Int,
        *,
        is_normalized: Bool,
    ):
        """Call this with is_normalized=True if you know the values are already in the correct range.
        """

        if not is_normalized:
            var extra_seconds: Int
            extra_seconds, microseconds = divmod(microseconds, 1000000)
            seconds += extra_seconds

            var extra_days: Int
            extra_days, seconds = divmod(seconds, 24 * 60 * 60)
            days += extra_days

        custom_debug_assert(
            0 <= microseconds < 1000000,
            "microseconds should be in the range [0, 1000000[",
        )
        custom_debug_assert(
            0 <= seconds < 24 * 60 * 60,
            "seconds should be in the range [0, 24 * 60 * 60[",
        )
        custom_debug_assert(
            -99999999 <= days <= 999999999,
            "days should be in the range -999999999 to 999999999",
        )

        self.days = days
        self.seconds = seconds
        self.microseconds = microseconds

    # use @value when https://github.com/modularml/mojo/issues/1705 is fixed
    fn __copyinit__(inout self, existing: Self):
        self.days = existing.days
        self.seconds = existing.seconds
        self.microseconds = existing.microseconds

    fn __moveinit__(inout self, owned existing: Self):
        self.days = existing.days
        self.seconds = existing.seconds
        self.microseconds = existing.microseconds

    fn __repr__(self) -> String:
        var args = List[String]()
        if self.days:
            args.append("days=" + str(self.days))
        if self.seconds:
            args.append("seconds=" + str(self.seconds))
        if self.microseconds:
            args.append("microseconds=" + str(self.microseconds))
        if len(args) == 0:
            args.append("0")
        return "datetime.timedelta(" + join(", ", args) + ")"

    fn __str__(self) -> String:
        var mm: Int
        var ss: Int
        var hh: Int
        mm, ss = divmod(self.seconds, 60)
        hh, mm = divmod(mm, 60)
        var s = str(hh)
        s += ":" + rjust(str(mm), 2, "0")
        s += ":" + rjust(str(ss), 2, "0")
        if self.days:
            var plural: String = ""
            if abs(self.days) != 1:
                plural = "s"
            s = str(self.days) + " day" + plural + ", " + s
        if self.microseconds:
            s = s + "." + rjust(str(self.microseconds), 6, "0")
        return s

    fn total_seconds(self) -> Int:
        """Total seconds in the duration."""
        return (
            (self.days * 86400 + self.seconds) * 10**6 + self.microseconds
        ) // 10**6

    fn __add__(self, other: timedelta) -> timedelta:
        return timedelta(
            self.days + other.days,
            self.seconds + other.seconds,
            self.microseconds + other.microseconds,
        )

    fn __sub__(self, other: timedelta) -> timedelta:
        return timedelta(
            self.days - other.days,
            self.seconds - other.seconds,
            self.microseconds - other.microseconds,
        )

    fn __neg__(self) -> timedelta:
        return timedelta(-self.days, -self.seconds, -self.microseconds)

    fn __pos__(self) -> timedelta:
        return self

    def __abs__(self) -> timedelta:
        if self.days < 0:
            return -self
        else:
            return self

    fn __mul__(self, other: Int) -> timedelta:
        return timedelta(
            self.days * other, self.seconds * other, self.microseconds * other
        )

    # TODO: support multiplying by a float

    fn _to_microseconds(self) -> Int64:
        # we return an Int64 because the result may overflow a 32-bit int
        return (self.days * (24 * 3600) + self.seconds) * 1000000 + self.microseconds

    fn __floordiv__(self, other: timedelta) -> Int64:
        return self._to_microseconds() // other._to_microseconds()

    fn __floordiv__(self, other: Int) -> timedelta:
        return timedelta(0, 0, (self._to_microseconds() // other).to_int())

    fn __truediv__(self, other: timedelta) -> Float64:
        return (
            self._to_microseconds().cast[DType.float64]()
            / other._to_microseconds().cast[DType.float64]()
        )

    fn __truediv__(self, other: Int) -> timedelta:
        return timedelta(
            microseconds=(
                self._to_microseconds().cast[DType.float64]() / Float64(other)
            ).to_int()
        )

    # TODO: divide by a float
    #    if isinstance(other, float):
    #        a, b = other.as_integer_ratio()
    #        return timedelta(0, 0, _divide_and_round(b * usec, a))

    fn __mod__(self, other: timedelta) -> timedelta:
        var r = self._to_microseconds() % other._to_microseconds()
        return timedelta(0, 0, r.to_int())

    fn __divmod__(self, other: timedelta) -> Tuple[Int, timedelta]:
        var q: Int64
        var r: Int64
        q, r = divmod(self._to_microseconds(), other._to_microseconds())
        return q.to_int(), timedelta(0, 0, r.to_int())

    # Comparisons of timedelta objects with other.
    # functools.total_ordering would be useful here if available

    fn __eq__(self, other: timedelta) -> Bool:
        return (
            self.days == other.days
            and self.seconds == other.seconds
            and self.microseconds == other.microseconds
        )

    fn __ne__(self, other: timedelta) -> Bool:
        return not (self == other)

    fn __le__(self, other: timedelta) -> Bool:
        return self._cmp(other) <= 0

    fn __lt__(self, other: timedelta) -> Bool:
        return self._cmp(other) < 0

    fn __ge__(self, other: timedelta) -> Bool:
        return self._cmp(other) >= 0

    fn __gt__(self, other: timedelta) -> Bool:
        return self._cmp(other) > 0

    fn _cmp(self, other: timedelta) -> Int:
        return _cmp_list(self._getstate(), other._getstate())

    fn __hash__(self) -> Int:
        return custom_hash(self._getstate())

    fn __bool__(self) -> Bool:
        return self.days != 0 or self.seconds != 0 or self.microseconds != 0

    @always_inline
    fn _getstate(self) -> List[Int]:
        return List[Int](self.days, self.seconds, self.microseconds)


--- stdlib_extensions/datetime/_timezone.mojo ---

from ..builtins import Optional
from .. import datetime as dt
from ..builtins.string import rjust


@value
struct timezone(CollectionElement, Stringable, Hashable):
    var _offset: timedelta
    var _name: Optional[String]

    alias _maxoffset = timedelta(hours=24, microseconds=-1)
    alias _minoffset = -timezone._maxoffset
    alias utc = timezone(timedelta(0), None)

    # bpo-37642: These attributes are rounded to the nearest minute for backwards
    # compatibility, even though the constructor will accept a wider range of
    # values. This may change in the future.
    alias min = timezone(-timedelta(hours=23, minutes=59))
    alias max = timezone(timedelta(hours=23, minutes=59))

    fn __init__(inout self, offset: timedelta, name: Optional[String] = None):
        # if not cls._minoffset <= offset <= cls._maxoffset:
        #     raise ValueError("offset must be a timedelta "
        #                      "strictly between -timedelta(hours=24) and "
        #                      "timedelta(hours=24).")
        self._offset = offset
        self._name = name

    fn __eq__(self, other: timezone) -> Bool:
        return self._offset == other._offset

    fn __hash__(self) -> Int:
        return self._offset.__hash__()

    fn __repr__(self) -> String:
        """Convert to formal string, for repr().

        >>> tz = timezone.utc
        >>> repr(tz)
        'datetime.timezone.utc'
        >>> tz = timezone(timedelta(hours=-5), 'EST')
        >>> repr(tz)
        "datetime.timezone(datetime.timedelta(-1, 68400), 'EST')"
        """
        # TODO: enable when https://github.com/modularml/mojo/issues/1787 is fixed
        # if self == timezone.utc:
        if self._offset == timedelta(0):
            return "datetime.timezone.utc"

        var result: String = "datetime.timezone(" + self._offset.__repr__()
        if self._name is not None:
            result += ", '" + self._name.value() + "'"
        return result + ")"

    fn __str__(self) -> String:
        return self.tzname(None)

    fn utcoffset(self, dt: Optional[datetime]) -> timedelta:
        return self._offset

    fn tzname(self, dt: Optional[datetime]) -> String:
        if self._name is None:
            return self._name_from_offset(self._offset)
        else:
            return self._name.value()

    fn dst(self, dt: Optional[datetime]) -> None:
        return None

    # fn fromutc(self, dt: datetime) -> datetime:
    #    #if dt.tzinfo is not self:
    #    #    raise ValueError("fromutc: dt.tzinfo "
    #    #                     "is not self")
    #    return dt + self._offset

    @staticmethod
    fn _name_from_offset(owned delta: timedelta) -> String:
        if not delta:
            return "UTC"
        var sign: String
        if delta < timedelta(0):
            sign = "-"
            delta = -delta
        else:
            sign = "+"
        # can use divmod later when we support non-register-passable for Tuple
        var hours = delta // timedelta(hours=1)
        var rest = delta % timedelta(hours=1)
        var minutes = rest // timedelta(minutes=1)
        rest = rest % timedelta(minutes=1)
        var seconds = rest.seconds
        var microseconds = rest.microseconds
        var result = "UTC" + sign + rjust(str(hours), 2, "0") + ":" + rjust(
            str(minutes), 2, "0"
        )
        if seconds or microseconds:
            result += ":" + rjust(str(seconds), 2, "0")
        if microseconds:
            result += "." + rjust(str(microseconds), 6, "0")
        return result


alias UTC = timezone.utc


--- stdlib_extensions/datetime/_tzinfo.mojo ---

trait tzinfo(CollectionElement):
    """Abstract base class for time zone info classes.

    Subclasses must override the tzname(), utcoffset(), dst(), and fromutc() methods.
    """

    fn tzname(self, dt: datetime) -> String:
        """Name of time zone."""
        ...

    fn utcoffset(self, dt: datetime) -> timedelta:
        """Positive for east of UTC, negative for west of UTC."""
        ...

    fn dst(self, dt: datetime) -> timedelta:
        """From datetime -> DST offset as timedelta, positive for east of UTC.

        Return 0 if DST not in effect.  utcoffset() must include the DST
        offset.
        """
        ...

    # fn fromutc(self: Self, dt: datetime) -> datetime[Self]:
    #    # use the default function below
    #    ...


# def fromutc[T: tzinfo](self: T, dt: datetime[T]) -> datetime:
#    """From datetime in UTC to datetime in local time."""
#    if dt.tzinfo is not self:
#        raise ValueError("dt.tzinfo is not self")
#    dtoff = dt.utcoffset()
#    if dtoff is None:
#        raise ValueError("fromutc() requires a non-None utcoffset() "
#                         "result")
#    # See the long comment block at the end of this file for an
#    # explanation of this algorithm.
#    dtdst = dt.dst()
#    if dtdst is None:
#        raise ValueError("fromutc() requires a non-None dst() result")
#    delta = dtoff - dtdst
#    if delta:
#        dt += delta
#        dtdst = dt.dst()
#        if dtdst is None:
#            raise ValueError("fromutc(): dt.dst gave inconsistent "
#                             "results; cannot convert")
#    return dt + dtdst


--- stdlib_extensions/datetime/_utils.mojo ---

"""Concrete date/time and related types.

See http://www.iana.org/time-zones/repository/tz-link.html for
time zone and DST data sources.

This file is taken from https://github.com/python/cpython/blob/main/Lib/_pydatetime.py
It's just been converted to Mojo manually.
"""

from ..builtins import divmod
from ..builtins.string import join
from ..time import struct_time
import math as _math
import sys
from ..builtins import Optional, bytes
from ..builtins._generic_list import _cmp_list
from ..builtins import custom_hash
from ..builtins import bool_to_int
from .._utils import custom_debug_assert
from utils.static_tuple import StaticTuple


def _cmp(x, y):
    return 0 if x == y else 1 if x > y else -1


alias MINYEAR = 1
alias MAXYEAR = 9999
alias MAXORDINAL = 3652059  # date.max.toordinal()

# Utility functions, adapted from Python's Demo/classes/Dates.py, which
# also assumes the current Gregorian calendar indefinitely extended in
# both directions.  Difference:  Dates.py calls January 1 of year 0 day
# number 1.  The code here calls January 1 of year 1 day number 1.  This is
# to match the definition of the "proleptic Gregorian" calendar in Dershowitz
# and Reingold's "Calendrical Calculations", where it's the base calendar
# for all computations.  See the book for algorithms for converting between
# proleptic Gregorian ordinals and many other calendar systems.

# -1 is a placeholder for indexing purposes.


alias DAYS_IN_MONTH = StaticTuple[Int, 13](
    -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
)


fn _get_days_before_month() -> List[Int]:
    var result = List[Int]()
    result.append(-1)  # -1 is a placeholder for indexing purposes.
    var dbm = 0
    for i in range(1, len(DAYS_IN_MONTH)):
        var dim = DAYS_IN_MONTH[i]
        result.append(dbm)
        dbm += dim
    return result


alias DAYS_BEFORE_MONTH = _get_days_before_month()


fn _is_leap(year: Int) -> Bool:
    "year -> 1 if leap year, else 0."
    return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)


fn _days_before_year(year: Int) -> Int:
    "year -> number of days before January 1st of year."
    var y = year - 1
    return y * 365 + y // 4 - y // 100 + y // 400


fn _days_in_month(year: Int, month: Int) -> Int:
    "year, month -> number of days in that month in that year."
    # assert 1 <= month <= 12, month
    if month == 2 and _is_leap(year):
        return 29
    return DAYS_IN_MONTH[month]


fn _bool_to_int(x: Bool) -> Int:
    """Remove when Bool is Intable"""
    if x:
        return 1
    else:
        return 0


fn _days_before_month(year: Int, month: Int) -> Int:
    "year, month -> number of days in year preceding first day of month."
    # assert 1 <= month <= 12, 'month must be in 1..12'
    return DAYS_BEFORE_MONTH[month] + _bool_to_int(month > 2 and _is_leap(year))


fn ymd2ord(year: Int, month: Int, day: Int) -> Int:
    "Year, month, day -> ordinal, considering 01-Jan-0001 as day 1."
    var dim = _days_in_month(year, month)
    return _days_before_year(year) + _days_before_month(year, month) + day


alias _DI400Y = _days_before_year(401)  # number of days in 400 years
alias _DI100Y = _days_before_year(101)  #    "    "   "   " 100   "
alias _DI4Y = _days_before_year(5)  #    "    "   "   "   4   "

# A 4-year cycle has an extra leap day over what we'd get from pasting
# together 4 single years.

# assert _DI4Y == 4 * 365 + 1

# Similarly, a 400-year cycle has an extra leap day over what we'd get from
# pasting together 4 100-year cycles.

# assert _DI400Y == 4 * _DI100Y + 1

# OTOH, a 100-year cycle has one fewer leap day than we'd get from
# pasting together 25 4-year cycles.

# assert _DI100Y == 25 * _DI4Y - 1


fn ord2ymd(owned n: Int) -> Tuple[Int, Int, Int]:
    "Ordinal -> (year, month, day), considering 01-Jan-0001 as day 1."
    # n is a 1-based index, starting at 1-Jan-1.  The pattern of leap years
    # repeats exactly every 400 years.  The basic strategy is to find the
    # closest 400-year boundary at or before n, then work with the offset
    # from that boundary to n.  Life is much clearer if we subtract 1 from
    # n first -- then the values of n at 400-year boundaries are exactly
    # those divisible by _DI400Y:
    #
    #     D  M   Y            n              n-1
    #     -- --- ----        ----------     ----------------
    #     31 Dec -400        -_DI400Y       -_DI400Y -1
    #      1 Jan -399         -_DI400Y +1   -_DI400Y      400-year boundary
    #     ...
    #     30 Dec  000        -1             -2
    #     31 Dec  000         0             -1
    #      1 Jan  001         1              0            400-year boundary
    #      2 Jan  001         2              1
    #      3 Jan  001         3              2
    #     ...
    #     31 Dec  400         _DI400Y        _DI400Y -1
    #      1 Jan  401         _DI400Y +1     _DI400Y      400-year boundary
    n -= 1
    var n400: Int
    n400, n = divmod(n, _DI400Y)
    var year = n400 * 400 + 1  # ..., -399, 1, 401, ...
    # Now n is the (non-negative) offset, in days, from January 1 of year, to
    # the desired date.  Now compute how many 100-year cycles precede n.
    # Note that it's possible for n100 to equal 4!  In that case 4 full
    # 100-year cycles precede the desired day, which implies the desired
    # day is December 31 at the end of a 400-year cycle.
    var n100: Int
    n100, n = divmod(n, _DI100Y)
    # Now compute how many 4-year cycles precede it.
    var n4: Int
    n4, n = divmod(n, _DI4Y)
    # And now how many single years.  Again n1 can be 4, and again meaning
    # that the desired day is December 31 at the end of the 4-year cycle.
    var n1: Int
    n1, n = divmod(n, 365)
    year += n100 * 100 + n4 * 4 + n1
    if n1 == 4 or n100 == 4:
        # assert n == 0
        return year - 1, 12, 31
    # Now the year is correct, and n is the offset from January 1.  We find
    # the month via an estimate that's either exact or one too large.
    var leapyear = n1 == 3 and (n4 != 24 or n100 == 3)
    # assert leapyear == _is_leap(year)
    var month = (n + 50) >> 5
    var preceding = DAYS_BEFORE_MONTH[month] + _bool_to_int(month > 2 and leapyear)
    if preceding > n:  # estimate is too large
        month -= 1
        preceding -= DAYS_IN_MONTH[month] + _bool_to_int(month == 2 and leapyear)
    n -= preceding
    # assert 0 <= n < _days_in_month(year, month)
    # Now the year and month are correct, and n is the offset from the
    # start of that month:  we're done!
    return year, month, n + 1


# Month and day names.  For localized versions, see the calendar module.
alias MONTHS_SHORT_NAMES = StaticTuple[StringLiteral, 13](
    "",
    "Jan",
    "Feb",
    "Mar",
    "Apr",
    "May",
    "Jun",
    "Jul",
    "Aug",
    "Sep",
    "Oct",
    "Nov",
    "Dec",
)

alias MONTHS_NAMES = StaticTuple[StringLiteral, 13](
    "",
    "January",
    "February",
    "March",
    "April",
    "May",
    "June",
    "July",
    "August",
    "September",
    "October",
    "November",
    "December",
)


alias DAYS_SHORT_NAMES = StaticTuple[StringLiteral, 8](
    "",
    "Mon",
    "Tue",
    "Wed",
    "Thu",
    "Fri",
    "Sat",
    "Sun",
)

alias DAYS_NAMES = StaticTuple[StringLiteral, 8](
    "",
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday",
    "Sunday",
)


fn _build_struct_time(
    y: Int, m: Int, d: Int, hh: Int, mm: Int, ss: Int, dstflag: Int
) -> struct_time:
    var wday = (ymd2ord(y, m, d) + 6) % 7
    var dnum = _days_before_month(y, m) + d
    return struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag))


## Correctly substitute for %z and %Z escapes in strftime formats.
# def _wrap_strftime(object, format, timetuple):
#    # Don't call utcoffset() or tzname() unless actually needed.
#    freplace = None  # the string to use for %f
#    zreplace = None  # the string to use for %z
#    colonzreplace = None  # the string to use for %:z
#    Zreplace = None  # the string to use for %Z
#
#    # Scan format for %z, %:z and %Z escapes, replacing as needed.
#    newformat = []
#    i, n = 0, len(format)
#    while i < n:
#        ch = format[i]
#        i += 1
#        if ch == '%':
#            if i < n:
#                ch = format[i]
#                i += 1
#                if ch == 'f':
#                    if freplace is None:
#                        freplace = '%06d' % getattr(object,
#                                                    'microsecond', 0)
#                    newformat.append(freplace)
#                elif ch == 'z':
#                    if zreplace is None:
#                        if hasattr(object, "utcoffset"):
#                            zreplace = _format_offset(object.utcoffset(), sep="")
#                        else:
#                            zreplace = ""
#                    assert '%' not in zreplace
#                    newformat.append(zreplace)
#                elif ch == ':':
#                    if i < n:
#                        ch2 = format[i]
#                        i += 1
#                        if ch2 == 'z':
#                            if colonzreplace is None:
#                                if hasattr(object, "utcoffset"):
#                                    colonzreplace = _format_offset(object.utcoffset(), sep=":")
#                                else:
#                                    colonzreplace = ""
#                            assert '%' not in colonzreplace
#                            newformat.append(colonzreplace)
#                        else:
#                            newformat.append('%')
#                            newformat.append(ch)
#                            newformat.append(ch2)
#                elif ch == 'Z':
#                    if Zreplace is None:
#                        Zreplace = ""
#                        if hasattr(object, "tzname"):
#                            s = object.tzname()
#                            if s is not None:
#                                # strftime is going to have at this: escape %
#                                Zreplace = s.replace('%', '%%')
#                    newformat.append(Zreplace)
#                else:
#                    newformat.append('%')
#                    newformat.append(ch)
#            else:
#                newformat.append('%')
#        else:
#            newformat.append(ch)
#    newformat = "".join(newformat)
#    return _time.strftime(newformat, timetuple)
#
fn _is_ascii_digit(c: String) -> Bool:
    alias my_str: String = "0123456789"
    for i in range(len(my_str)):
        if c == my_str[i]:
            return True
    return False


#
# def _find_isoformat_datetime_separator(dtstr):
#    # See the comment in _datetimemodule.c:_find_isoformat_datetime_separator
#    len_dtstr = len(dtstr)
#    if len_dtstr == 7:
#        return 7
#
#    assert len_dtstr > 7
#    date_separator = "-"
#    week_indicator = "W"
#
#    if dtstr[4] == date_separator:
#        if dtstr[5] == week_indicator:
#            if len_dtstr < 8:
#                raise ValueError("Invalid ISO string")
#            if len_dtstr > 8 and dtstr[8] == date_separator:
#                if len_dtstr == 9:
#                    raise ValueError("Invalid ISO string")
#                if len_dtstr > 10 and _is_ascii_digit(dtstr[10]):
#                    # This is as far as we need to resolve the ambiguity for
#                    # the moment - if we have YYYY-Www-##, the separator is
#                    # either a hyphen at 8 or a number at 10.
#                    #
#                    # We'll assume it's a hyphen at 8 because it's way more
#                    # likely that someone will use a hyphen as a separator than
#                    # a number, but at this point it's really best effort
#                    # because this is an extension of the spec anyway.
#                    # TODO(pganssle): Document this
#                    return 8
#                return 10
#            else:
#                # YYYY-Www (8)
#                return 8
#        else:
#            # YYYY-MM-DD (10)
#            return 10
#    else:
#        if dtstr[4] == week_indicator:
#            # YYYYWww (7) or YYYYWwwd (8)
#            idx = 7
#            while idx < len_dtstr:
#                if not _is_ascii_digit(dtstr[idx]):
#                    break
#                idx += 1
#
#            if idx < 9:
#                return idx
#
#            if idx % 2 == 0:
#                # If the index of the last number is even, it's YYYYWwwd
#                return 7
#            else:
#                return 8
#        else:
#            # YYYYMMDD (8)
#            return 8
#
#
fn _parse_isoformat_date(dtstr: String) raises -> Tuple[Int, Int, Int]:
    # It is assumed that this is an ASCII-only string of lengths 7, 8 or 10,
    # see the comment on Modules/_datetimemodule.c:_find_isoformat_datetime_separator
    # assert len(dtstr) in (7, 8, 10)
    var year = atol(dtstr[0:4])
    var has_sep = dtstr[4] == "-"
    var pos = 4 + bool_to_int(has_sep)
    if dtstr[pos : pos + 1] == "W":
        # YYYY-?Www-?D?
        pos += 1
        var weekno = atol(dtstr[pos : pos + 2])
        pos += 2
        var dayno = 1
        if len(dtstr) > pos:
            if (dtstr[pos : pos + 1] == "-") != has_sep:
                raise Error("Inconsistent use of dash separator")
            pos += bool_to_int(has_sep)
            dayno = atol(dtstr[pos : pos + 1])
        return _isoweek_to_gregorian(year, weekno, dayno)
    else:
        var month = atol(dtstr[pos : pos + 2])
        pos += 2
        if (dtstr[pos : pos + 1] == "-") != has_sep:
            raise Error("Inconsistent use of dash separator")
        pos += bool_to_int(has_sep)
        var day = atol(dtstr[pos : pos + 2])
        return year, month, day


# tuple[int, int, int] -> tuple[int, int, int] version of date.fromisocalendar
fn _isoweek_to_gregorian(year: Int, week: Int, day: Int) -> Tuple[Int, Int, Int]:
    # Year is bounded this way because 9999-12-31 is (9999, 52, 5)
    # if not MINYEAR <= year <= MAXYEAR:
    #    raise ValueError(f"Year is out of range: {year}")
    if not 0 < week < 53:
        var out_of_range = True
        if week == 53:
            # ISO years have 53 weeks in them on years starting with a
            # Thursday and leap years starting on a Wednesday
            var first_weekday = ymd2ord(year, 1, 1) % 7
            if first_weekday == 4 or (first_weekday == 3 and _is_leap(year)):
                out_of_range = False
        # if out_of_range:
        #    raise ValueError(f"Invalid week: {week}")
    # if not 0 < day < 8:
    #    raise ValueError(f"Invalid weekday: {day} (range is [1, 7])")
    # Now compute the offset from (Y, 1, 1) in days:
    var day_offset = (week - 1) * 7 + (day - 1)
    # Calculate the ordinal day for monday, week 1
    var day_1 = isoweek1monday(year)
    var ord_day = day_1 + day_offset
    return ord2ymd(ord_day)


## name is the offset-producing method, "utcoffset" or "dst".
## offset is what it returned.
## If offset isn't None or timedelta, raises TypeError.
## If offset is None, returns None.
## Else offset is checked for being in range.
## If it is, its integer value is returned.  Else ValueError is raised.
fn _check_utc_offset(name: String, offset: Optional[timedelta]):
    custom_debug_assert(
        name == "utcoffset" or name == "dst", "name must be 'utcoffset' or 'dst'"
    )
    if offset is None:
        return
    custom_debug_assert(
        -timedelta(1) < offset.value() < timedelta(1),
        name
        + "()="
        + offset.value()
        + ", must be strictly between -timedelta(hours=24) and timedelta(hours=24)",
    )


fn _check_date_fields(year: Int, month: Int, day: Int):
    # TODO: remove this and use the alias when we don't get "no expansion found" errors
    custom_debug_assert(
        MINYEAR <= year <= MAXYEAR, "year must be in the range 1 to 9999"
    )
    custom_debug_assert(1 <= month <= 12, "month must be in 1..12")
    var dim = _days_in_month(year, month)
    custom_debug_assert(1 <= day <= dim, "day must be in 1.." + str(dim))


fn _check_time_fields(hour: Int, minute: Int, second: Int, microsecond: Int, fold: Int):
    custom_debug_assert(0 <= hour <= 23, "hour must be in 0..23")
    custom_debug_assert(0 <= minute <= 59, "minute must be in 0..59")
    custom_debug_assert(0 <= second <= 59, "second must be in 0..59")
    custom_debug_assert(0 <= microsecond <= 999999, "microsecond must be in 0..999999")
    custom_debug_assert(0 <= fold <= 1, "fold must be in 0 or 1")


# def _divide_and_round(a, b):
#    """divide a by b and round result to the nearest integer
#
#    When the ratio is exactly half-way between two integers,
#    the even integer is returned.
#    """
#    # Based on the reference implementation for divmod_near
#    # in Objects/longobject.c.
#    q, r = divmod(a, b)
#    # round up if either r / b > 0.5, or r / b == 0.5 and q is odd.
#    # The expression r / b > 0.5 is equivalent to 2 * r > b if b is
#    # positive, 2 * r < b if b negative.
#    r *= 2
#    greater_than_half = r > b if b > 0 else r < b
#    if greater_than_half or r == b and q % 2 == 1:
#        q += 1
#
#    return q
#
#


#
#

#


#


#
#
fn isoweek1monday(year: Int) -> Int:
    # Helper to calculate the day number of the Monday starting week 1
    # XXX This could be done more efficiently
    var THURSDAY = 3
    var firstday = ymd2ord(year, 1, 1)
    var firstweekday = (firstday + 6) % 7  # See weekday() above
    var week1monday = firstday - firstweekday
    if firstweekday > THURSDAY:
        week1monday += 7
    return week1monday


--- stdlib_extensions/os/__init__.mojo ---

from ..syscalls import process, filesystem
from ..syscalls import random as syscalls_random
from pathlib import Path
from ..builtins import bytes


fn getpid() -> Int:
    return process.getpid().to_int()


fn fspath(path: String) -> String:
    return path


fn fspath(path: StringLiteral) -> String:
    return String(path)


fn fspath(path: Path) -> String:
    # should actually return __fspath__ but we don't have that yet
    return path.__str__()


fn rmdir(path: String):
    return filesystem.rmdir(path)


fn rmdir(path: StringLiteral):
    return rmdir(fspath(path))


fn rmdir(path: Path):
    return rmdir(fspath(path))


fn unlink(path: String):
    return filesystem.unlink(path)


fn unlink(path: StringLiteral):
    return unlink(fspath(path))


fn unlink(path: Path):
    return unlink(fspath(path))


fn urandom(size: Int) -> bytes:
    # TODO: we currently use the getrandom syscalls, but it will work only in linux.
    # to be compatible with other systems, we should follow what cpython does, which is by order
    # of priority:
    # BCryptGenRandom() on Windows
    # getrandom() function (ex: Linux and Solaris)
    # getentropy() function (ex: OpenBSD)
    # /dev/urandom device

    return syscalls_random.getrandom(size)


--- stdlib_extensions/pathlib/__init__.mojo ---

from ._path import Path


--- stdlib_extensions/pathlib/_path.mojo ---

from pathlib import Path as MojoStdPath
from pathlib import cwd
from ..os import unlink, rmdir
from ..builtins.string import endswith


@value
struct Path:
    var _mojo_std_path: MojoStdPath

    # here it's just a copy of what's in the stdlib since
    # we can't inherit :(
    fn __init__(inout self: Self) raises -> None:
        self._mojo_std_path = MojoStdPath()

    fn __init__(inout self: Self, path: StringLiteral):
        self._mojo_std_path = MojoStdPath(path)

    fn __init__(inout self: Self, path: MojoStdPath):
        self._mojo_std_path = path

    fn __init__(inout self: Self, path: String):
        self._mojo_std_path = MojoStdPath(path)

    fn __init__(inout self: Self, path: StringRef):
        self._mojo_std_path = MojoStdPath(path)

    fn __truediv__(self: Self, suffix: Path) -> Self:
        return self / suffix.__fspath__()

    fn __truediv__(self: Self, suffix: StringLiteral) -> Self:
        return self.__truediv__(String(suffix))

    fn __truediv__(self: Self, suffix: StringRef) -> Self:
        return self.__truediv__(String(suffix))

    fn __truediv__(self: Self, suffix: String) -> Self:
        var ends_with_slash: Bool
        ends_with_slash = endswith(self.__fspath__(), "/")
        if ends_with_slash:
            return Path(self.__fspath__() + suffix)
        else:
            return Path(self.__fspath__() + "/" + suffix)

    fn __fspath__(self) -> String:
        return self._mojo_std_path.__str__()

    fn __str__(self: Self) -> String:
        return self.__fspath__()

    fn __repr__(self: Self) -> String:
        return self.__fspath__()

    @staticmethod
    fn cwd() raises -> Self:
        return Path(cwd())

    # now functions that don't have a direct equivalent in the stdlib
    fn open(self: Self, mode: StringLiteral) raises -> FileHandle:
        return open(self.__str__(), mode)

    # TODO: fuse those when we have unions
    fn write_text(self: Self, text: StringLiteral) raises -> None:
        with self.open("w") as f:
            f.write(text)

    fn write_text(self: Self, text: StringRef) raises -> None:
        with self.open("w") as f:
            f.write(text)

    fn write_text(self: Self, text: String) raises -> None:
        with self.open("w") as f:
            f.write(text)

    fn read_text(self: Self) raises -> String:
        with self.open("r") as f:
            return f.read()

    fn unlink(self: Self) -> None:
        return unlink(self.__str__())

    fn rmdir(self: Self) -> None:
        return rmdir(self.__str__())


--- stdlib_extensions/stdlib_tests/__init__.mojo ---



--- stdlib_extensions/stdlib_tests/builtins/__init__.mojo ---



--- stdlib_extensions/stdlib_tests/builtins/test_bytes.mojo ---

from ...builtins import bytes, to_bytes
from ..utils import assert_equal


def test_bytes_operations_indexing_and_add():
    some_bytes = bytes(10)

    assert_equal(len(some_bytes), 10)

    for i in range(10):
        some_bytes[i] = i

    assert_equal(some_bytes[5], 5)

    new_vector = List[UInt8](100, 101, 102)

    new_bytes = bytes(new_vector)
    assert_equal(new_bytes[2], 102)

    combinaison = some_bytes + new_bytes
    assert_equal(combinaison[12], 102)
    assert_equal(len(combinaison), 13)
    assert_equal(len(some_bytes), 10)

    some_bytes += new_bytes
    assert_equal(some_bytes[12], 102)
    assert_equal(len(some_bytes), 13)


def test_bytes_operations_multiplying():
    some_bytes = bytes(10)

    assert_equal(len(some_bytes), 10)

    for i in range(10):
        some_bytes[i] = i

    new_bytes = some_bytes * 3

    assert_equal(len(new_bytes), 30)
    assert_equal(new_bytes[12], 2)
    assert_equal(len(some_bytes), 10)  # unchanged

    new_bytes = some_bytes * 0
    assert_equal(len(new_bytes), 0)

    some_bytes *= 4
    assert_equal(len(some_bytes), 40)
    assert_equal(some_bytes[34], 4)

    some_bytes *= 0
    assert_equal(len(some_bytes), 0)


def test_bytes_hex():
    some_bytes = bytes.fromhex("00")
    assert_equal(len(some_bytes), 1)
    assert_equal(some_bytes[0], 0)

    some_bytes = bytes.fromhex("01")
    assert_equal(len(some_bytes), 1)
    assert_equal(some_bytes[0], 1)

    some_bytes = bytes.fromhex("0A")
    assert_equal(len(some_bytes), 1)
    assert_equal(some_bytes[0], 10)

    some_bytes = bytes.fromhex("0F")
    assert_equal(len(some_bytes), 1)
    assert_equal(some_bytes[0], 15)

    some_bytes = bytes.fromhex("10")
    assert_equal(len(some_bytes), 1)
    assert_equal(some_bytes[0], 16)

    some_bytes = bytes.fromhex("FF")
    assert_equal(len(some_bytes), 1)
    assert_equal(some_bytes[0], 255)

    some_bytes = bytes.fromhex("0000")
    assert_equal(len(some_bytes), 2)
    assert_equal(some_bytes[0], 0)
    assert_equal(some_bytes[1], 0)

    some_bytes = bytes.fromhex("1010")
    assert_equal(len(some_bytes), 2)
    assert_equal(some_bytes[0], 16)
    assert_equal(some_bytes[1], 16)

    some_bytes = bytes.fromhex("FFFF")
    assert_equal(len(some_bytes), 2)
    assert_equal(some_bytes[0], 255)
    assert_equal(some_bytes[1], 255)


def test_convert_to_hex():
    assert_equal(bytes(0).hex(), "")
    assert_equal(bytes(1).hex(), "00")
    assert_equal(bytes(2).hex(), "0000")
    assert_equal(bytes(3).hex(), "000000")

    assert_equal(bytes.fromhex("ab").hex(), "ab")
    assert_equal(bytes.fromhex("abcd").hex(), "abcd")
    assert_equal(bytes.fromhex("abcdef").hex(), "abcdef")
    assert_equal(bytes.fromhex("abcdef12").hex(), "abcdef12")
    assert_equal(bytes.fromhex("abcdef1234").hex(), "abcdef1234")


def test_to_bytes():
    # TODO fix bug here
    a = bytes.fromhex("03")
    assert_equal(bytes.fromhex("03"), to_bytes(3))
    assert_equal(bytes.fromhex("0003"), to_bytes(3, length=2))
    assert_equal(bytes.fromhex("0300"), to_bytes(3, length=2, byteorder="little"))
    assert_equal(bytes.fromhex("03e8"), to_bytes(1000, length=2, byteorder="big"))
    assert_equal(bytes.fromhex("e803"), to_bytes(1000, length=2, byteorder="little"))
    assert_equal(bytes.fromhex("e80300"), to_bytes(1000, length=3, byteorder="little"))
    assert_equal(bytes.fromhex("00"), to_bytes(0))


def run_tests():
    test_bytes_operations_indexing_and_add()
    test_bytes_operations_multiplying()
    test_bytes_hex()
    test_convert_to_hex()
    test_to_bytes()


--- stdlib_extensions/stdlib_tests/builtins/test_hex.mojo ---

from ...builtins import hex
from ..utils import assert_equal


def test_convert_uint8_to_hex():
    assert_equal(hex(0), "0x00")
    assert_equal(hex(1), "0x01")
    assert_equal(hex(15), "0x0f")
    assert_equal(hex(16), "0x10")
    assert_equal(hex(255), "0xff")
    assert_equal(hex(200), "0xc8")
    assert_equal(hex(0x34), "0x34")


def run_tests():
    test_convert_uint8_to_hex()


--- stdlib_extensions/stdlib_tests/builtins/test_math.mojo ---

from ...stdlib_tests.utils import assert_true, assert_false, assert_equal
from ...builtins import divmod


def test_divmod():
    var a: Int
    var b: Int
    a, b = divmod(10, 4)
    assert_equal(a, 2)
    assert_equal(b, 2)

    a, b = divmod(-11, -2)
    assert_equal(a, 5)
    assert_equal(b, -1)


def run_tests():
    test_divmod()


--- stdlib_extensions/stdlib_tests/builtins/test_string.mojo ---

from ...stdlib_tests.utils import assert_true, assert_false, assert_equal
from ...builtins.string import (
    endswith,
    rjust,
    ljust,
    split,
    replace,
    removeprefix,
    expandtabs,
    removesuffix,
    startswith,
    join,
    rstrip,
    lstrip,
    strip,
    _ALL_WHITESPACES,
)
from ...builtins import list_to_str


def test_ljust():
    assert_equal(ljust("hello", 10), "hello     ")
    assert_equal(ljust("hello", 10, "x"), "helloxxxxx")
    assert_equal(ljust("hello", 5), "hello")
    assert_equal(ljust("hello", 3), "hello")
    assert_equal(ljust("hello", 3, "x"), "hello")


def test_rjust():
    assert_equal(rjust("hello", 10), "     hello")
    assert_equal(rjust("hello", 10, "x"), "xxxxxhello")
    assert_equal(rjust("hello", 5), "hello")
    assert_equal(rjust("hello", 3), "hello")
    assert_equal(rjust("hello", 3, "x"), "hello")


def test_endswith():
    assert_true(endswith("hello world", "world"), "endswith 1 failed")
    assert_true(endswith("hello world", "world", start=2), "endswith 2 failed")
    assert_true(endswith("hello world", "world", start=6), "endswith 3 failed")
    assert_true(endswith(" worldd", "world", start=0, end=6), "endswith 4 failed")
    assert_true(endswith(" worldd", "world", start=0, end=6), "endswith 5 failed")
    assert_false(endswith(" worldd", "world", start=0, end=7), "endswith 6 failed")
    assert_false(endswith(" worldd", "hello"), "endswith 7 failed")
    assert_false(endswith(" worldd", "world"), "endswith 8 failed")


def test_startswith():
    assert_true(startswith("hello world", "hello"), "startswith 1 failed")
    assert_true(startswith("hello world", "hello", start=0), "startswith 2 failed")
    assert_true(
        startswith("hello world", "hello", start=0, end=5), "startswith 3 failed"
    )
    assert_true(
        startswith("hello world", "hello", start=0, end=6), "startswith 4 failed"
    )
    assert_true(
        startswith("hello world", "hello", start=0, end=7), "startswith 5 failed"
    )
    assert_true(
        startswith("hello world", "hello", start=0, end=8), "startswith 6 failed"
    )
    assert_false(startswith("hello world", "world"), "startswith 7 failed")
    assert_false(startswith("hello world", "hello", start=1), "startswith 8 failed")
    assert_false(
        startswith("hello world", "hello", start=1, end=5), "startswith 9 failed"
    )
    assert_false(
        startswith("hello world", "hello", start=1, end=6), "startswith 10 failed"
    )
    assert_false(
        startswith("hello world", "hello", start=1, end=7), "startswith 11 failed"
    )
    assert_false(
        startswith("hello world", "hello", start=1, end=8), "startswith 12 failed"
    )


def test_split():
    assert_equal(list_to_str(split("hello world")), "['hello', 'world']")
    assert_equal(list_to_str(split("Hello world")), "['Hello', 'world']")
    assert_equal(list_to_str(split("Hello world", maxsplit=1)), "['Hello', 'world']")
    assert_equal(
        list_to_str(split("apple::banana::orange", sep="::")),
        "['apple', 'banana', 'orange']",
    )
    assert_equal(
        list_to_str(split("apple::banana::orange", sep="::", maxsplit=1)),
        "['apple', 'banana::orange']",
    )
    assert_equal(
        list_to_str(split("a--b--c--d", sep="--", maxsplit=2)), "['a', 'b', 'c--d']"
    )


def test_join_simple():
    input_list = List[String]()
    input_list.append("hello")
    input_list.append("Mojo 🔥")
    input_list.append("world")

    assert_equal(join(" ", input_list), "hello Mojo 🔥 world")
    assert_equal(join(", ", input_list), "hello, Mojo 🔥, world")
    assert_equal(join("::", input_list), "hello::Mojo 🔥::world")
    assert_equal(join("", input_list), "helloMojo 🔥world")


def test_join_edge_case():
    input_list = List[String]()

    assert_equal(join(" ", input_list), "")
    assert_equal(join(", ", input_list), "")
    assert_equal(join("::", input_list), "")
    assert_equal(join("", input_list), "")


def test_replace():
    assert_equal(replace("hello world", "world", "there"), "hello there")
    assert_equal(replace("hello world", "world", "there", 1), "hello there")
    assert_equal(replace("hello world", "world", "there", 2), "hello there")

    assert_equal(replace("hello world world", "world", "there", 1), "hello there world")
    assert_equal(replace("hello world world", "world", "there", 2), "hello there there")
    assert_equal(
        replace("hello 0 world world", "world", "there", 0), "hello 0 world world"
    )
    assert_equal(
        replace("hello -1 world world", "world", "there", -1), "hello -1 there there"
    )
    assert_equal(
        replace("hello None world world", "world", "there"), "hello None there there"
    )


def test_removeprefix():
    assert_equal(removeprefix("hello world", "hello"), " world")
    assert_equal(removeprefix("hello world", "world"), "hello world")
    assert_equal(removeprefix("hello world", "hello world"), "")
    assert_equal(removeprefix("hello world", "llo wor"), "hello world")


def test_removesuffix():
    assert_equal(removesuffix("hello world", "world"), "hello ")
    assert_equal(removesuffix("hello world", "hello"), "hello world")
    assert_equal(removesuffix("hello world", "hello world"), "")
    assert_equal(removesuffix("hello world", "llo wor"), "hello world")


def test_expandtabs():
    assert_equal(expandtabs("hello\tworld", 8), "hello        world")
    assert_equal(expandtabs("hello\tworld", 4), "hello    world")
    assert_equal(expandtabs("hello\tworld", 2), "hello  world")
    assert_equal(expandtabs("helloworld", 2), "helloworld")
    assert_equal(expandtabs("hello\tworld", 0), "helloworld")


def test_rstrip():
    assert_equal(rstrip(" hello world "), " hello world")
    assert_equal(rstrip(" hello world ", " "), " hello world")
    assert_equal(rstrip(" hello world ", "d "), " hello worl")
    assert_equal(rstrip(" hello world ", "d"), " hello world ")
    assert_equal(rstrip(" hello world ", " d"), " hello worl")  # order doesn't matter
    assert_equal(
        rstrip(" \t\n\r\x0b\f hello world  \t\n\r\x0b\f"), " \t\n\r\x0b\f hello world"
    )


def test_lstrip():
    assert_equal(lstrip(" hello world "), "hello world ")
    assert_equal(lstrip(" hello world ", " "), "hello world ")
    assert_equal(lstrip(" hello world ", "d "), "hello world ")
    assert_equal(lstrip(" hello world ", "h"), " hello world ")
    assert_equal(lstrip(" hello world ", "h "), "ello world ")  # order doesn't matter
    assert_equal(lstrip(" hello world ", " h"), "ello world ")
    assert_equal(lstrip("\thello world \t"), "hello world \t")
    assert_equal(
        lstrip(" \t\n\r\x0b\fhello world \t\n\r\x0b\f"), "hello world \t\n\r\x0b\f"
    )


def test_strip():
    assert_equal(strip(" hello world "), "hello world")
    assert_equal(strip(" hello world ", " "), "hello world")
    assert_equal(strip(" hello world ", "d "), "hello worl")
    assert_equal(strip(" hello world ", "d"), " hello world ")
    assert_equal(strip(" hello world ", " d"), "hello worl")  # order doesn't matter
    assert_equal(strip(" \t\n\r\x0b\f hello world  \t\n\r\x0b\f"), "hello world")


def run_tests():
    test_ljust()
    test_rjust()
    test_endswith()
    test_startswith()
    test_split()
    test_join_simple()
    test_join_edge_case()
    test_replace()
    test_removeprefix()
    test_removesuffix()
    test_expandtabs()
    test_rstrip()
    test_lstrip()
    test_strip()


--- stdlib_extensions/stdlib_tests/datetime/__init__.mojo ---



--- stdlib_extensions/stdlib_tests/datetime/test_date.mojo ---

from ...datetime import date
from ...stdlib_tests.utils import assert_true, assert_false, assert_equal
from python import Python


def test_date():
    var simple_date = date(2020, 1, 1)
    assert_equal(simple_date.year, 2020)
    assert_equal(simple_date.month, 1)
    assert_equal(simple_date.day, 1)
    assert_equal(simple_date.__str__(), "2020-01-01")
    assert_equal(simple_date.__repr__(), "datetime.date(2020, 1, 1)")
    assert_equal(
        simple_date.__str__(),
        str(Python.import_module("datetime").date(2020, 1, 1)),
    )

    # we'd be extremely unlucky if this fails
    assert_equal(
        date.today().__str__(), str(Python.import_module("datetime").date.today())
    )
    assert_equal(date.min.__repr__(), "datetime.date(1, 1, 1)")
    assert_equal(date.max.__repr__(), "datetime.date(9999, 12, 31)")


def test_date_hash():
    var some_time = date(2020, 1, 1)
    var some_time2 = date(2020, 1, 1)

    var some_other_time = date(2020, 1, 2)

    assert_equal(hash(some_time), hash(some_time2))
    assert_true(hash(some_time) != hash(some_other_time), "incorrect hash")
    assert_true(hash(some_time2) != hash(some_other_time), "incorrect hash 2")


def test_date_strftime():
    assert_equal(date(2020, 1, 1).strftime("%Y-%m-%d"), "2020-01-01")
    assert_equal(date(2020, 1, 1).strftime("%Y-%m-%d %H:%M:%S"), "2020-01-01 00:00:00")


def test_full_date_strftime():
    # all codes
    # TODO: Add U, W, G, V and :z
    var format: String = "%a|%A|%w|%d|%b|%B|%m|%y|%Y|%H|%I|%p|%M|%S|%f|%z|%Z|%j|%c|%x|%X|%%|%u|%:z"
    var expected: String = "Tue|Tuesday|2|02|Mar|March|03|21|2021|00|12|AM|00|00|000000|||061|Tue Mar  2 00:00:00 2021|03/02/21|00:00:00|%|2|"
    assert_equal(date(2021, 3, 2).strftime(format), expected)


def test_ctime():
    assert_equal(date(2021, 3, 2).ctime(), "Tue Mar  2 00:00:00 2021")
    assert_equal(date(1821, 1, 10).ctime(), "Wed Jan 10 00:00:00 1821")


def test_fromisoformat():
    """Examples taken from the python documentation."""
    assert_equal(date.fromisoformat("2019-12-04"), date(2019, 12, 4))
    assert_equal(date.fromisoformat("20191204"), date(2019, 12, 4))
    assert_equal(date.fromisoformat("2021-W01-1"), date(2021, 1, 4))


def run_tests():
    test_date()
    test_date_hash()
    test_date_strftime()
    test_full_date_strftime()
    test_ctime()
    test_fromisoformat()


--- stdlib_extensions/stdlib_tests/datetime/test_datetime.mojo ---

from ...datetime import datetime, timezone, timedelta
from ...stdlib_tests.utils import assert_true, assert_false, assert_equal
from python import Python


def test_aliases():
    assert_equal(datetime.min.year, 1)
    assert_equal(datetime.max.year, 9999)

    # TODO: test resolution when it's possible, currently it's broken because of
    # https://github.com/modularml/mojo/issues/1787


def test_constructor_default():
    a = datetime(2020, 3, 4)
    assert_equal(a.year, 2020)
    assert_equal(a.month, 3)
    assert_equal(a.day, 4)
    assert_equal(a.hour, 0)
    assert_equal(a.minute, 0)
    assert_equal(a.second, 0)
    assert_equal(a.microsecond, 0)
    assert_true(a.tzinfo is None, "tzinfo should be None")
    assert_equal(a.fold, 0)


def test_constructor_all_values():
    a = datetime(2020, 3, 4, 5, 6, 7, 8, tzinfo=timezone(timedelta(hours=-1)), fold=1)
    assert_equal(a.year, 2020)
    assert_equal(a.month, 3)
    assert_equal(a.day, 4)
    assert_equal(a.hour, 5)
    assert_equal(a.minute, 6)
    assert_equal(a.second, 7)
    assert_equal(a.microsecond, 8)
    assert_true(a.tzinfo is not None, "tzinfo should not be None")
    assert_equal(a.tzinfo.value(), timezone(timedelta(hours=-1)))
    assert_equal(a.fold, 1)


def test_datetime_get_timetuple():
    a = datetime(2020, 3, 4, 5, 6, 7, 8, tzinfo=timezone(timedelta(hours=-1)), fold=1)
    timestruct = a.timetuple()
    assert_equal(timestruct.tm_year, 2020)
    assert_equal(timestruct.tm_mon, 3)
    assert_equal(timestruct.tm_mday, 4)
    assert_equal(timestruct.tm_hour, 5)
    assert_equal(timestruct.tm_min, 6)
    assert_equal(timestruct.tm_sec, 7)
    assert_equal(timestruct.tm_wday, 2)
    assert_equal(timestruct.tm_yday, 64)
    assert_equal(timestruct.tm_isdst, -1)


def test_datetime_replace():
    a = datetime(2020, 3, 4, 5, 6, 7, 8, tzinfo=timezone(timedelta(hours=-1)), fold=1)
    a = a.replace(year=2021)
    assert_equal(a.year, 2021)
    assert_equal(a.month, 3)
    assert_equal(a.day, 4)

    a = a.replace(month=9)
    assert_equal(a.year, 2021)
    assert_equal(a.month, 9)

    a = a.replace(day=15)
    assert_equal(a.year, 2021)
    assert_equal(a.month, 9)
    assert_equal(a.day, 15)

    a = a.replace(hour=20)
    assert_equal(a.year, 2021)
    assert_equal(a.month, 9)
    assert_equal(a.day, 15)
    assert_equal(a.hour, 20)
    assert_equal(a.minute, 6)

    a = a.replace(minute=30)
    assert_equal(a.minute, 30)

    a = a.replace(second=1)
    assert_equal(a.second, 1)

    a = a.replace(microsecond=2)
    assert_equal(a.microsecond, 2)

    a = a.replace(tzinfo=None)
    assert_true(a.tzinfo is None, "tzinfo should be None")

    a = a.replace(tzinfo=timezone(timedelta(hours=-1)))
    assert_true(a.tzinfo is not None, "tzinfo should not be None")
    assert_equal(a.tzinfo.value(), timezone(timedelta(hours=-1)))

    a = a.replace(fold=0)
    assert_equal(a.fold, 0)


def test_datetime_repr():
    a = datetime(2020, 3, 4, 5, 6, 7, 8, tzinfo=timezone(timedelta(hours=-1)), fold=1)
    assert_equal(
        a.__repr__(),
        (
            "datetime.datetime(2020, 3, 4, 5, 6, 7, 8,"
            " tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=82800)),"
            " fold=1)"
        ),
    )

    a = datetime(2020, 3, 4)
    assert_equal(a.__repr__(), "datetime.datetime(2020, 3, 4, 0, 0)")


def test_datetime_add():
    # This could be refactored using str(datetime)
    a = datetime(2020, 3, 4, 5, 6, 7, 8, tzinfo=timezone(timedelta(hours=-1)), fold=1)
    b = timedelta(days=1)
    c = a + b
    assert_equal(c.year, 2020)
    assert_equal(c.month, 3)
    assert_equal(c.day, 5)
    assert_equal(c.hour, 5)
    assert_equal(c.minute, 6)
    assert_equal(c.second, 7)
    assert_equal(c.microsecond, 8)
    assert_equal(c.tzinfo.value(), timezone(timedelta(hours=-1)))
    assert_equal(c.fold, 0)

    a = datetime(2021, 2, 3)
    b = timedelta(days=365)
    c = a + b
    assert_equal(c.year, 2022)
    assert_equal(c.month, 2)
    assert_equal(c.day, 3)

    a = datetime(2020, 3, 4, 5, 6, 7, 8)
    b = timedelta(days=1, minutes=120, seconds=3602)
    c = a + b
    assert_equal(c.year, 2020)
    assert_equal(c.month, 3)
    assert_equal(c.day, 5)
    assert_equal(c.hour, 8)
    assert_equal(c.minute, 6)
    assert_equal(c.second, 9)
    assert_equal(c.microsecond, 8)
    assert_true(c.tzinfo is None, "tzinfo is None")


def test_datetime_sub():
    # This could be refactored using str(datetime)
    a = datetime(2020, 3, 4, 5, 6, 7, 8, tzinfo=timezone(timedelta(hours=-1)), fold=1)
    b = timedelta(days=1)
    c = a - b
    assert_equal(c.year, 2020)
    assert_equal(c.month, 3)
    assert_equal(c.day, 3)
    assert_equal(c.hour, 5)
    assert_equal(c.minute, 6)
    assert_equal(c.second, 7)
    assert_equal(c.microsecond, 8)
    assert_equal(c.tzinfo.value(), timezone(timedelta(hours=-1)))
    assert_equal(c.fold, 0)

    a = datetime(2021, 2, 3)
    b = timedelta(days=365)
    c = a - b
    assert_equal(c.year, 2020)
    assert_equal(c.month, 2)
    assert_equal(c.day, 4)
    a = datetime(2020, 3, 4, 5, 6, 7, 8)
    b = timedelta(days=1, minutes=120, seconds=3602)
    c = a - b
    assert_equal(c.year, 2020)
    assert_equal(c.month, 3)
    assert_equal(c.day, 3)
    assert_equal(c.hour, 2)
    assert_equal(c.minute, 6)
    assert_equal(c.second, 5)
    assert_equal(c.microsecond, 8)
    assert_true(c.tzinfo is None, "tzinfo is None")


def test_datetime_now():
    var python_datetime_module = Python.import_module("datetime")
    var now1 = python_datetime_module.datetime.now()
    var now2 = datetime.now()
    var now3 = python_datetime_module.datetime.now()

    var now2_as_py = now2.to_python()

    assert_true(
        str(now1 <= now2_as_py) == "True", "datetimes should be in order, 1 <= 2"
    )
    assert_true(
        str(now2_as_py <= now3) == "True", "datetimes should be in order, 2 <= 3"
    )


def test_datetime_isoformat():
    a = datetime(
        2020,
        3,
        4,
        5,
        6,
        7,
        8,
        tzinfo=timezone(timedelta(hours=-1, minutes=8, seconds=-9)),
        fold=1,
    )
    assert_equal(a.isoformat(), "2020-03-04T05:06:07.000008-00:52:09")
    assert_equal(a.isoformat(sep="U"), "2020-03-04U05:06:07.000008-00:52:09")
    assert_equal(
        a.isoformat(sep="U", timespec="seconds"), "2020-03-04U05:06:07-00:52:09"
    )
    assert_equal(a.isoformat(sep="U", timespec="minutes"), "2020-03-04U05:06-00:52:09")
    assert_equal(a.isoformat(sep="U", timespec="hours"), "2020-03-04U05-00:52:09")

    a = datetime(2022, 3, 4)
    assert_equal(a.isoformat(), "2022-03-04T00:00:00")


def test_datetime_str():
    a = datetime(
        2020,
        3,
        4,
        5,
        6,
        7,
        8,
        tzinfo=timezone(timedelta(hours=-1, minutes=8, seconds=-9)),
        fold=1,
    )
    assert_equal(str(a), "2020-03-04 05:06:07.000008-00:52:09")


def run_tests():
    test_aliases()
    test_constructor_default()
    test_constructor_all_values()
    test_datetime_get_timetuple()
    test_datetime_replace()
    test_datetime_repr()
    test_datetime_add()
    test_datetime_sub()
    test_datetime_now()
    test_datetime_isoformat()
    test_datetime_str()


--- stdlib_extensions/stdlib_tests/datetime/test_time_class.mojo ---

from ...datetime import time, timezone, timedelta
from ...stdlib_tests.utils import assert_true, assert_false, assert_equal
from ...builtins import Optional


def test_time_creation():
    t = time(12, 30, 0)
    assert_equal(t.hour, 12)
    assert_equal(t.minute, 30)
    assert_equal(t.second, 0)
    assert_equal(t.microsecond, 0)
    assert_true(t.tzinfo is None, "tzinfo is None")
    assert_equal(t.fold, 0)


def test_time_repr():
    var utc = timezone(timedelta(0))
    assert_equal(time(12, 30, 0).__repr__(), "datetime.time(12, 30)")
    assert_equal(
        time(12, 30, 0, tzinfo=utc).__repr__(),
        "datetime.time(12, 30, tzinfo=datetime.timezone.utc)",
    )
    assert_equal(time(12, 30, 0, fold=1).__repr__(), "datetime.time(12, 30, fold=1)")
    assert_equal(
        time(12, 30, 0, tzinfo=utc, fold=1).__repr__(),
        "datetime.time(12, 30, tzinfo=datetime.timezone.utc, fold=1)",
    )
    assert_equal(time(12, 30, 8).__repr__(), "datetime.time(12, 30, 8)")
    assert_equal(
        time(12, 30, 8, tzinfo=utc).__repr__(),
        "datetime.time(12, 30, 8, tzinfo=datetime.timezone.utc)",
    )
    assert_equal(time(12, 30, 8, 4).__repr__(), "datetime.time(12, 30, 8, 4)")
    assert_equal(
        time(12, 30, 8, 4, tzinfo=utc).__repr__(),
        "datetime.time(12, 30, 8, 4, tzinfo=datetime.timezone.utc)",
    )
    assert_equal(
        time(12, 30, 8, 4, fold=1).__repr__(), "datetime.time(12, 30, 8, 4, fold=1)"
    )
    assert_equal(
        time(12, 30, 8, 4, tzinfo=utc, fold=1).__repr__(),
        "datetime.time(12, 30, 8, 4, tzinfo=datetime.timezone.utc, fold=1)",
    )


def test_utcoffset():
    var t = time(12, 30, 0)
    assert_true(t.utcoffset() is None, "utcoffset is None")
    assert_true(
        time(12, 30, 0, tzinfo=timezone(timedelta(0))).utcoffset() is not None,
        "utcoffset should not be None",
    )
    assert_true(
        time(12, 30, 0, tzinfo=timezone(timedelta(0))).utcoffset().value()
        == timedelta(0),
        "utcoffset is 0",
    )
    assert_true(
        time(12, 30, 0, tzinfo=timezone(timedelta(hours=-4))).utcoffset().value()
        == timedelta(hours=-4),
        "utcoffset is 0",
    )


def test_comparison_without_timezone():
    assert_true(time(12, 30, 0) == time(12, 30, 0), "the two times are equal")
    assert_false(time(12, 30, 0) == time(12, 30, 1), "the two times are not equal")

    assert_true(time(12, 30, 0) != time(12, 30, 1), "the two times are not equal")
    assert_false(time(12, 30, 0) != time(12, 30, 0), "the two times are equal")

    assert_true(
        time(12, 30, 0) < time(12, 30, 1), "the first time is less than the second"
    )
    assert_false(
        time(12, 30, 1) < time(12, 30, 0), "the first time is not less than the second"
    )

    assert_true(
        time(12, 30, 0) <= time(12, 30, 1), "the first time is less than the second"
    )
    assert_false(
        time(12, 30, 1) <= time(12, 30, 0), "the first time is not less than the second"
    )
    assert_true(
        time(12, 30, 1) <= time(12, 30, 1),
        "the first time is less or equal to the second",
    )

    assert_true(
        time(12, 30, 1) > time(12, 30, 0), "the first time is greater than the second"
    )
    assert_false(
        time(12, 30, 0) > time(12, 30, 1),
        "the first time is not greater than the second",
    )

    assert_true(
        time(12, 30, 1) >= time(12, 30, 0), "the first time is greater than the second"
    )
    assert_false(
        time(12, 30, 0) >= time(12, 30, 1),
        "the first time is not greater than the second",
    )
    assert_true(
        time(12, 30, 1) >= time(12, 30, 1),
        "the first time is greater or equal to the second",
    )


def test_dst():
    assert_true(time(12, 30, 0).dst() is None, "dst is None")
    assert_true(
        time(12, 30, 0, tzinfo=timezone(timedelta(0))).dst() is None,
        "dst should be None",
    )
    assert_true(
        time(12, 30, 0, tzinfo=timezone(timedelta(hours=-4))).dst() is None,
        "dst is None",
    )


def test_hash_time():
    var t1 = time(12, 30, 0)
    var t2 = time(12, 30, 0)
    assert_true(t1 == t2, "the two times are equal")
    assert_true(hash(t1) == hash(t2), "the two hashes are equal")


def test_hash_time_with_timezone():
    var t1 = time(12, 30, 0, tzinfo=timezone(timedelta(0)))
    var t2 = time(12, 30, 0, tzinfo=timezone(timedelta(0)))
    assert_true(t1 == t2, "the two times are equal")
    assert_true(hash(t1) == hash(t2), "the two hashes are equal")


def test_hash_time_with_different_timezones():
    var t1 = time(12, 30, 0, tzinfo=timezone(timedelta(0)))
    var t2 = time(12, 30, 0, tzinfo=timezone(timedelta(hours=-4, minutes=-3)))
    assert_false(t1 == t2, "the two times should not be equal")
    assert_false(hash(t1) == hash(t2), "the two hashes are not equal")


def test_hash_time_with_different_timezones_equal():
    var t1 = time(12, 30, 0, tzinfo=timezone(timedelta(0)))
    var t2 = time(8, 40, 0, tzinfo=timezone(timedelta(hours=-4, minutes=10)))
    assert_true(t1 == t2, "the two times are equal")
    assert_true(hash(t1) == hash(t2), "the two hashes should be equal")


def test_time_isoformat_default():
    assert_equal(time(12, 30, 0).isoformat(), "12:30:00")
    assert_equal(
        time(12, 30, 0, tzinfo=timezone(timedelta(0))).isoformat(), "12:30:00+00:00"
    )
    assert_equal(
        time(12, 30, 0, tzinfo=timezone(timedelta(hours=-4))).isoformat(),
        "12:30:00-04:00",
    )
    assert_equal(time(12, 30, 0, fold=1).isoformat(), "12:30:00")
    assert_equal(
        time(12, 30, 0, tzinfo=timezone(timedelta(0)), fold=1).isoformat(),
        "12:30:00+00:00",
    )
    assert_equal(
        time(12, 30, 0, 100, tzinfo=timezone(timedelta(hours=-4)), fold=1).isoformat(),
        "12:30:00.000100-04:00",
    )


def test_isoformat_with_different_timespec():
    t = time(12, 30, 8, 108, tzinfo=timezone(timedelta(hours=-4)), fold=1)
    assert_equal(t.isoformat(), "12:30:08.000108-04:00")
    assert_equal(t.isoformat("hours"), "12-04:00")
    assert_equal(t.isoformat("minutes"), "12:30-04:00")
    assert_equal(t.isoformat("seconds"), "12:30:08-04:00")
    assert_equal(t.isoformat("milliseconds"), "12:30:08.000-04:00")
    assert_equal(t.isoformat("microseconds"), "12:30:08.000108-04:00")


def test_str_function_on_time():
    """Should return isoformat."""
    assert_equal(
        str(time(12, 30, 0, 105, tzinfo=timezone(timedelta(hours=-4)), fold=1)),
        "12:30:00.000105-04:00",
    )
    assert_equal(str(time(12, 30, 0)), "12:30:00")


def test_time_strftime_all_values_filled():
    t = time(
        12,
        30,
        0,
        105,
        tzinfo=timezone(timedelta(hours=-4, minutes=1, seconds=8, microseconds=33)),
        fold=1,
    )
    format = "%a|%A|%w|%d|%b|%B|%m|%y|%Y|%H|%I|%p|%M|%S|%f|%z|%Z|%j|%U|%W|%c|%x|%X|%G|%u|%V|%%|%:z"
    expected = (
        "Mon|Monday|1|01|Jan|January|01|00|1900|12|12|PM|30|00|000105|-035851.999967|UTC-03:58:51.999967|001|00|01|Mon"
        " Jan  1 12:30:00 1900|01/01/00|12:30:00|1900|1|01|%|-03:58:51.999967"
    )
    assert_equal(t.strftime(format), expected)


def test_time_strftime_simple_time():
    t = time(1, 3)
    format = "%a|%A|%w|%d|%b|%B|%m|%y|%Y|%H|%I|%p|%M|%S|%f|%z|%Z|%j|%U|%W|%c|%x|%X|%G|%u|%V|%%|%:z"
    expected = (
        "Mon|Monday|1|01|Jan|January|01|00|1900|01|01|AM|03|00|000000|||001|00|01|Mon"
        " Jan  1 01:03:00 1900|01/01/00|01:03:00|1900|1|01|%|"
    )
    assert_equal(t.strftime(format), expected)


def test_time_format_dunder_empty():
    # empty is the same as isoformat
    t = time(
        12,
        30,
        0,
        105,
        tzinfo=timezone(timedelta(hours=-4, minutes=1, seconds=8, microseconds=33)),
        fold=1,
    )
    assert_equal(t.__format__(""), t.isoformat())


def test_time_format_dunder_non_empty():
    # this should be the same as strftime
    t = time(
        1,
        59,
        59,
        999999,
        tzinfo=timezone(timedelta(hours=4, minutes=1, seconds=8, microseconds=33)),
        fold=1,
    )
    format = "%a|%A|%w|%d|%b|%B|%m|%y|%Y|%H|%I|%p|%M|%S|%f|%z|%Z|%j|%U|%W|%c|%x|%X|%G|%u|%V|%%|%:z"
    expected = (
        "Mon|Monday|1|01|Jan|January|01|00|1900|01|01|AM|59|59|999999|+040108.000033|UTC+04:01:08.000033|001|00|01|Mon"
        " Jan  1 01:59:59 1900|01/01/00|01:59:59|1900|1|01|%|+04:01:08.000033"
    )
    assert_equal(t.__format__(format), expected)


def test_time_fromisoformat():
    assert_equal(time.fromisoformat("12:30:00"), time(12, 30, 0))
    assert_equal(
        time.fromisoformat("12:30:00+00:00"),
        time(12, 30, 0, tzinfo=timezone(timedelta(0))),
    )
    assert_equal(
        time.fromisoformat("12:30:00-04:00"),
        time(12, 30, 0, tzinfo=timezone(timedelta(hours=-4))),
    )

    assert_equal(
        time.fromisoformat("12:30:00.000100-04:00"),
        time(12, 30, 0, 100, tzinfo=timezone(timedelta(hours=-4))),
    )
    assert_equal(
        time.fromisoformat("12:30:00.000100+04:00:08.000003"),
        time(
            12,
            30,
            0,
            100,
            tzinfo=timezone(timedelta(hours=4, seconds=8, microseconds=3)),
        ),
    )
    assert_equal(
        time.fromisoformat("123000.000100+040008.000003"),
        time(
            12,
            30,
            0,
            100,
            tzinfo=timezone(timedelta(hours=4, seconds=8, microseconds=3)),
        ),
    )

    # those examples come from the Python docs:
    assert_equal(time.fromisoformat("04:23:01"), time(4, 23, 1))
    assert_equal(time.fromisoformat("T04:23:01"), time(4, 23, 1))
    assert_equal(time.fromisoformat("T042301"), time(4, 23, 1))
    assert_equal(time.fromisoformat("04:23:01.000384"), time(4, 23, 1, 384))
    assert_equal(time.fromisoformat("04:23:01,000384"), time(4, 23, 1, 384))
    assert_equal(
        time.fromisoformat("04:23:01+04:00"),
        time(4, 23, 1, tzinfo=timezone(timedelta(seconds=14400))),
    )
    assert_equal(
        time.fromisoformat("04:23:01Z"), time(4, 23, 1, tzinfo=timezone(timedelta(0)))
    )
    assert_equal(
        time.fromisoformat("04:23:01+00:00"),
        time(4, 23, 1, tzinfo=timezone(timedelta(0))),
    )


def run_tests():
    test_time_creation()
    test_time_repr()
    test_utcoffset()
    test_comparison_without_timezone()
    test_dst()
    test_hash_time()
    test_hash_time_with_timezone()
    test_hash_time_with_different_timezones()
    test_hash_time_with_different_timezones_equal()
    test_time_isoformat_default()
    test_isoformat_with_different_timespec()
    test_str_function_on_time()
    test_time_strftime_all_values_filled()
    test_time_strftime_simple_time()
    test_time_format_dunder_empty()
    test_time_format_dunder_non_empty()
    test_time_fromisoformat()


--- stdlib_extensions/stdlib_tests/datetime/test_timedelta.mojo ---

from ...stdlib_tests.utils import assert_true, assert_false, assert_equal
from ...datetime import timedelta


def test_timedelta():
    assert_equal(timedelta().total_seconds(), 0)

    divided = timedelta(hours=1) / timedelta(seconds=1)
    assert_equal(String(divided), "3600.0")


def test_timedelta_constructor():
    one_min = timedelta(minutes=1)
    assert_equal(one_min.seconds, 60)
    assert_equal(one_min.days, 0)
    assert_equal(one_min.microseconds, 0)

    half_a_min = timedelta(minutes=0.5, use_floats=True)
    assert_equal(half_a_min.seconds, 30)
    assert_equal(half_a_min.days, 0)
    assert_equal(half_a_min.microseconds, 0)

    one_day_and_a_half = timedelta(days=1.5, use_floats=True)
    assert_equal(one_day_and_a_half.days, 1)
    assert_equal(one_day_and_a_half.seconds, 43200)
    assert_equal(one_day_and_a_half.microseconds, 0)

    two_weeks = timedelta(weeks=2)
    assert_equal(two_weeks.days, 14)
    assert_equal(two_weeks.seconds, 0)
    assert_equal(two_weeks.microseconds, 0)


def test_timedelta_repr():
    assert_equal(timedelta().__repr__(), "datetime.timedelta(0)")
    assert_equal(timedelta(days=1).__repr__(), "datetime.timedelta(days=1)")
    assert_equal(timedelta(minutes=1).__repr__(), "datetime.timedelta(seconds=60)")
    assert_equal(timedelta(seconds=1).__repr__(), "datetime.timedelta(seconds=1)")
    assert_equal(
        timedelta(microseconds=1).__repr__(), "datetime.timedelta(microseconds=1)"
    )
    assert_equal(
        timedelta(milliseconds=1).__repr__(), "datetime.timedelta(microseconds=1000)"
    )
    assert_equal(timedelta(hours=1).__repr__(), "datetime.timedelta(seconds=3600)")
    assert_equal(
        timedelta(days=1, hours=1).__repr__(),
        "datetime.timedelta(days=1, seconds=3600)",
    )
    assert_equal(
        timedelta(days=1, hours=1, minutes=1).__repr__(),
        "datetime.timedelta(days=1, seconds=3660)",
    )
    assert_equal(
        timedelta(days=1, hours=1, minutes=1, seconds=1).__repr__(),
        "datetime.timedelta(days=1, seconds=3661)",
    )
    assert_equal(
        timedelta(days=1, hours=1, minutes=1, seconds=1, microseconds=1).__repr__(),
        "datetime.timedelta(days=1, seconds=3661, microseconds=1)",
    )
    assert_equal(
        timedelta(
            days=1, hours=1, minutes=1, seconds=1, milliseconds=2, microseconds=1
        ).__repr__(),
        "datetime.timedelta(days=1, seconds=3661, microseconds=2001)",
    )


def run_tests():
    test_timedelta()
    test_timedelta_repr()
    test_timedelta_constructor()


--- stdlib_extensions/stdlib_tests/datetime/test_timezone.mojo ---

from ...datetime import time, timezone, timedelta
from ...stdlib_tests.utils import assert_true, assert_false, assert_equal
from ...builtins._types import Optional


def test_timezone_utc():
    a = timezone(timedelta(0))
    assert_equal(str(a), "UTC")
    # TODO: use when https://github.com/modularml/mojo/issues/1787 is fixed
    # assert_equal(timezone.utc, timezone(timedelta(0)))


def test_timezone_equality():
    assert_true(
        timezone(offset=timedelta(0)) == timezone(timedelta(0)),
        "timezones should be equal",
    )
    assert_true(
        timezone(offset=timedelta(0), name=String("dodo")) == timezone(timedelta(0)),
        "timezones should be equal",
    )
    assert_true(
        timezone(offset=timedelta(hours=-4), name=String("dodo"))
        == timezone(timedelta(hours=-4)),
        "timezones should be equal",
    )


def test_timezone_repr():
    assert_equal(
        timezone(offset=timedelta(hours=-4), name=String("dodo")).__repr__(),
        "datetime.timezone(datetime.timedelta(days=-1, seconds=72000), 'dodo')",
    )


def test_hash():
    assert_equal(hash(timezone(timedelta(0))), hash(timezone(timedelta(0))))
    assert_equal(
        hash(timezone(timedelta(0), name=String("dodo"))), hash(timezone(timedelta(0)))
    )

    assert_false(
        hash(timezone(timedelta(0))) == hash(timezone(timedelta(hours=1))),
        "timezones should not have the same hash",
    )
    assert_false(
        hash(timezone(timedelta(0), name=String("dodo")))
        == hash(timezone(timedelta(hours=1), name=String("dodo"))),
        "timezones should not have the same hash",
    )


def test_tzname():
    assert_equal(timezone(timedelta(0)).tzname(None), "UTC")
    assert_equal(timezone(timedelta(0), name=String("dodo")).tzname(None), "dodo")
    assert_equal(timezone(timedelta(hours=2)).tzname(None), "UTC+02:00")
    assert_equal(
        timezone(timedelta(hours=2, minutes=6, seconds=8, milliseconds=444)).tzname(
            None
        ),
        "UTC+02:06:08.444000",
    )

    assert_equal(str(timezone(timedelta(0))), "UTC")
    assert_equal(str(timezone(timedelta(0), name=String("dodo"))), "dodo")
    assert_equal(str(timezone(timedelta(hours=2))), "UTC+02:00")
    assert_equal(
        str(timezone(timedelta(hours=2, minutes=6, seconds=8, milliseconds=444))),
        "UTC+02:06:08.444000",
    )


def run_tests():
    test_timezone_utc()
    test_timezone_equality()
    test_timezone_repr()
    test_hash()
    test_tzname()


--- stdlib_extensions/stdlib_tests/datetime/test_utils.mojo ---

from ...stdlib_tests.utils import assert_true, assert_false, assert_equal
from ...datetime._utils import (
    _is_leap,
    _days_before_year,
    _days_in_month,
)


def test_is_leap():
    assert_true(_is_leap(4), "4 is a leap year")
    assert_true(_is_leap(400), "400 is a leap year")
    assert_false(_is_leap(100), "100 is not a leap year")
    assert_false(_is_leap(200), "200 is not a leap year")
    assert_false(_is_leap(300), "300 is not a leap year")
    assert_false(_is_leap(500), "500 is not a leap year")
    assert_true(_is_leap(40), "400 is a leap year")
    assert_true(_is_leap(8), "8 is a leap year")
    assert_false(_is_leap(1), "1 is not a leap year")


def test_days_before_year():
    assert_equal(_days_before_year(1), 0)
    assert_equal(_days_before_year(2), 365)
    assert_equal(_days_before_year(3), 730)
    assert_equal(_days_before_year(4), 1095)
    assert_equal(_days_before_year(5), 1461)


def test_days_in_month():
    assert_equal(_days_in_month(1, 1), 31)
    assert_equal(_days_in_month(1, 2), 28)
    assert_equal(_days_in_month(4, 2), 29)
    assert_equal(_days_in_month(1, 3), 31)
    assert_equal(_days_in_month(1, 4), 30)
    assert_equal(_days_in_month(1, 5), 31)
    assert_equal(_days_in_month(1, 6), 30)
    assert_equal(_days_in_month(1, 7), 31)
    assert_equal(_days_in_month(1, 8), 31)
    assert_equal(_days_in_month(1, 9), 30)
    assert_equal(_days_in_month(1, 10), 31)
    assert_equal(_days_in_month(1, 11), 30)
    assert_equal(_days_in_month(1, 12), 31)
    assert_equal(_days_in_month(6, 3), 31)


def run_tests():
    test_is_leap()
    test_days_before_year()
    test_days_in_month()


--- stdlib_extensions/stdlib_tests/os/__init__.mojo ---



--- stdlib_extensions/stdlib_tests/os/test_process.mojo ---

from ...os import getpid, urandom
from ...builtins import bytes
from ..utils import assert_true, assert_equal


def test_getpid():
    current_pid = getpid()
    assert_true(current_pid > 0, "current pid should be above 0")


def test_urandom():
    some_random_bytes = urandom(10)
    assert_equal(len(some_random_bytes), 10)

    some_new_bytes = urandom(10)
    assert_true(some_random_bytes != some_new_bytes, "random bytes should be different")

    empty_bytes = urandom(0)
    assert_equal(len(empty_bytes), 0)


def run_tests():
    test_getpid()
    test_urandom()


--- stdlib_extensions/stdlib_tests/pathlib/__init__.mojo ---



--- stdlib_extensions/stdlib_tests/pathlib/test_path.mojo ---

from ...pathlib import Path
from ..utils import assert_equal


def test_read_and_write_from_file():
    # TODO: change this when we can create directories
    # TemporaryDirectory() would be very helpful to ensure cleanup
    tmp_dir = Path("/tmp/")
    tmp_file = tmp_dir / "test_file.txt"
    assert_equal(tmp_file.__fspath__(), "/tmp/test_file.txt")
    tmp_file.write_text("Hello mojo 🔥")
    assert_equal(tmp_file.read_text(), "Hello mojo 🔥")
    tmp_file.unlink()


def run_tests():
    test_read_and_write_from_file()


--- stdlib_extensions/stdlib_tests/time/__init__.mojo ---



--- stdlib_extensions/stdlib_tests/time/test_time.mojo ---

from ...time import time_ns
from python import Python
from ..utils import assert_true


def test_time_ns():
    py_time_ns = Python.import_module("time").time_ns

    t1 = py_time_ns()
    t_mojo = time_ns()
    t2 = py_time_ns()

    assert_true(
        t1.to_float64().cast[DType.int64]() <= t_mojo,
        "time_ns() should return a value equal or above the previous python call",
    )
    assert_true(
        t_mojo <= t2.to_float64().cast[DType.int64](),
        "time_ns() should return a value equal or below the next python call",
    )


def run_tests():
    test_time_ns()


--- stdlib_extensions/stdlib_tests/utils.mojo ---

"""Unlike what is present in the standard stdlib, we stop if something fails."""

from ..builtins import bytes


def assert_equal(a: bytes, b: bytes):
    if a != b:
        raise Error("Expected '" + a.hex() + "' to be equal to '" + b.hex() + "'")


def assert_equal(a: String, b: String):
    if a != b:
        raise Error("Expected '" + a + "' to be equal to '" + b + "'")


def assert_equal(a: Int, b: Int):
    if a != b:
        raise Error("Expected " + String(a) + " to be equal to " + String(b))


def assert_equal[T: Stringable, U: Stringable](a: T, b: U):
    if str(a) != str(b):
        raise Error(
            "Expected both to be equal.\n     got: " + str(a) + "\nExpected: " + str(b)
        )


def assert_true(value: Bool, message: String):
    if not value:
        raise Error(message)


def assert_false(value: Bool, message: String):
    if value:
        raise Error(message)


--- stdlib_extensions/stdlib_tests/uuid/__init__.mojo ---



--- stdlib_extensions/stdlib_tests/uuid/test_uuid_class.mojo ---

from ..utils import assert_equal, assert_true, assert_false, assert_false
from ...uuid import UUID, RFC_4122, uuid4
from ...builtins import bytes


def test_uuid_class_no_version():
    assert_equal(str(UUID(bytes=bytes(16))), "00000000-0000-0000-0000-000000000000")
    assert_equal(
        str(UUID("76fb1595-8b2f-456a-b809-bc2e00c70a45")),
        "76fb1595-8b2f-456a-b809-bc2e00c70a45",
    )

    some_uuid = UUID("162bb388-b33a-1fe3-be31-7e5993496eb8")
    assert_equal(str(some_uuid), "162bb388-b33a-1fe3-be31-7e5993496eb8")
    corresponding_bytes = some_uuid.bytes()
    assert_equal(len(corresponding_bytes), 16)
    assert_equal(str(UUID(corresponding_bytes)), str(some_uuid))

    assert_equal(
        UUID("76fb1595-8b2f-456a-b809-bc2e00c70a45").__repr__(),
        "UUID('76fb1595-8b2f-456a-b809-bc2e00c70a45')",
    )


def test_uuid_class_version_1():
    some_uuid = UUID("4c123f5a-86fa-11ee-a8d0-c3f648e463f5")
    assert_equal(some_uuid.variant(), RFC_4122)
    assert_equal(some_uuid.version(), 1)


def test_uuid_class_version_3():
    some_uuid = UUID("a3b9a1b0-8a53-3239-94cb-59bd25191542")
    assert_equal(some_uuid.variant(), RFC_4122)
    assert_equal(some_uuid.version(), 3)


def test_uuid_class_version_4():
    some_uuid = UUID("4d3a88c7-8a53-4239-94cb-59bd25191542")
    assert_equal(some_uuid.variant(), RFC_4122)
    assert_equal(some_uuid.version(), 4)


def test_uuid_class_version_5():
    some_uuid = UUID("a3b9a1b0-8a53-5239-94cb-59bd25191542")
    assert_equal(some_uuid.variant(), RFC_4122)
    assert_equal(some_uuid.version(), 5)


def test_set_version():
    for i in range(1, 6):
        assert_equal(
            UUID("4c123f5a-86fa-11ee-a8d0-c3f648e463f5", version=i).version(), i
        )
        assert_equal(
            UUID("a3b9a1b0-8a53-3239-94cb-59bd25191542", version=i).version(), i
        )
        assert_equal(
            UUID("4d3a88c7-8a53-4239-94cb-59bd25191542", version=i).version(), i
        )
        assert_equal(
            UUID("a3b9a1b0-8a53-5239-94cb-59bd25191542", version=i).version(), i
        )


def test_order():
    assert_true(
        UUID("00000000-0000-0000-0000-000000000000")
        < UUID("00000000-0000-0000-0000-000000000001"),
        "not less",
    )
    assert_false(
        UUID("00000000-0000-0000-0000-000000000000")
        > UUID("00000000-0000-0000-0000-000000000001"),
        "not greater",
    )
    assert_true(
        UUID("00000000-0000-0000-0000-000000000000")
        <= UUID("00000000-0000-0000-0000-000000000001"),
        "not less or equal",
    )
    assert_false(
        UUID("00000000-0000-0000-0000-000000000000")
        >= UUID("00000000-0000-0000-0000-000000000001"),
        "not greater or equal",
    )
    assert_true(
        UUID("00000000-0000-0000-0000-000000000000")
        == UUID("00000000-0000-0000-0000-000000000000"),
        "not equal",
    )
    assert_false(
        UUID("00000000-0000-0000-0000-000000000000")
        != UUID("00000000-0000-0000-0000-000000000000"),
        "not not equal",
    )

    assert_true(
        UUID("e0000000-0000-0000-0000-000000000000")
        < UUID("f0000000-0000-0000-0000-000000000000"),
        "not less",
    )
    assert_false(
        UUID("e0000000-0000-0000-0000-000000000000")
        > UUID("f0000000-0000-0000-0000-000000000000"),
        "not greater",
    )
    assert_true(
        UUID("e0000000-0000-0000-0000-000000000000")
        <= UUID("f0000000-0000-0000-0000-000000000000"),
        "not less or equal",
    )
    assert_false(
        UUID("e0000000-0000-0000-0000-000000000000")
        >= UUID("f0000000-0000-0000-0000-000000000000"),
        "not greater or equal",
    )
    assert_true(
        UUID("e0000000-0000-0000-0000-000000000000")
        == UUID("e0000000-0000-0000-0000-000000000000"),
        "not equal",
    )
    assert_false(
        UUID("e0000000-0000-0000-0000-000000000000")
        != UUID("e0000000-0000-0000-0000-000000000000"),
        "not not equal",
    )


def test_time_low():
    assert_equal(UUID("4c123f5a-86fa-11ee-a8d0-c3f648e463f5").time_low(), 1276264282)
    assert_equal(UUID("a3b9a1b0-8a53-3239-94cb-59bd25191542").time_low(), 2746851760)
    assert_equal(UUID("d3b9c1b0-8a53-5239-94cb-59bd25191542").time_low(), 3552166320)


def test_time_mid():
    assert_equal(UUID("4c123f5a-86fa-11ee-a8d0-c3f648e463f5").time_mid(), 34554)
    assert_equal(UUID("a3b9a1b0-8a53-3239-94cb-59bd25191542").time_mid(), 35411)
    assert_equal(UUID("d3b9c1b0-8a53-5239-94cb-59bd25191542").time_mid(), 35411)
    assert_equal(UUID("f588b9be-929a-4b15-925f-23c9903b847a").time_mid(), 37530)


def test_time_hi_version():
    assert_equal(UUID("4c123f5a-86fa-11ee-a8d0-c3f648e463f5").time_hi_version(), 4590)
    assert_equal(UUID("a3b9a1b0-8a53-3239-94cb-59bd25191542").time_hi_version(), 12857)
    assert_equal(UUID("d3b9c1b0-8a53-5239-94cb-59bd25191542").time_hi_version(), 21049)
    assert_equal(UUID("f588b9be-929a-4b15-925f-23c9903b847a").time_hi_version(), 19221)


def test_clock_seq_hi_variant():
    assert_equal(
        UUID("4c123f5a-86fa-11ee-a8d0-c3f648e463f5").clock_seq_hi_variant(), 168
    )
    assert_equal(
        UUID("a3b9a1b0-8a53-3239-94cb-59bd25191542").clock_seq_hi_variant(), 148
    )
    assert_equal(
        UUID("d3b9c1b0-8a53-5239-94cb-59bd25191542").clock_seq_hi_variant(), 148
    )
    assert_equal(
        UUID("f588b9be-929a-4b15-925f-23c9903b847a").clock_seq_hi_variant(), 146
    )


def test_clock_seq_low():
    assert_equal(UUID("4c123f5a-86fa-11ee-a8d0-c3f648e463f5").clock_seq_low(), 208)
    assert_equal(UUID("a3b9a1b0-8a53-3239-94cb-59bd25191542").clock_seq_low(), 203)
    assert_equal(UUID("d3b9c1b0-8a53-5239-94cb-59bd25191542").clock_seq_low(), 203)
    assert_equal(UUID("f588b9be-929a-4b15-925f-23c9903b847a").clock_seq_low(), 95)


def test_node():
    assert_equal(UUID("4c123f5a-86fa-11ee-a8d0-c3f648e463f5").node(), 215462552298485)
    assert_equal(UUID("a3b9a1b0-8a53-3239-94cb-59bd25191542").node(), 98668906091842)
    assert_equal(UUID("d3b9c1b0-8a53-5239-94cb-59bd25191542").node(), 98668906091842)
    assert_equal(UUID("f588b9be-929a-4b15-925f-23c9903b847a").node(), 39348615218298)


def test_urn():
    assert_equal(
        UUID("4c123f5a-86fa-11ee-a8d0-c3f648e463f5").urn(),
        "urn:uuid:4c123f5a-86fa-11ee-a8d0-c3f648e463f5",
    )
    assert_equal(
        UUID("a3b9a1b0-8a53-3239-94cb-59bd25191542").urn(),
        "urn:uuid:a3b9a1b0-8a53-3239-94cb-59bd25191542",
    )
    assert_equal(
        UUID("d3b9c1b0-8a53-5239-94cb-59bd25191542").urn(),
        "urn:uuid:d3b9c1b0-8a53-5239-94cb-59bd25191542",
    )
    assert_equal(
        UUID("f588b9be-929a-4b15-925f-23c9903b847a").urn(),
        "urn:uuid:f588b9be-929a-4b15-925f-23c9903b847a",
    )


def test_uuid4():
    for _ in range(100):
        assert_equal(uuid4().variant(), RFC_4122)
        assert_equal(uuid4().version(), 4)
        assert_false(uuid4() == uuid4(), "Randomy generated UUIDs are equal")


def run_tests():
    test_uuid_class_no_version()
    test_uuid_class_version_1()
    test_uuid_class_version_3()
    test_uuid_class_version_4()
    test_uuid_class_version_5()
    test_set_version()
    test_order()
    test_time_low()
    test_time_mid()
    test_time_hi_version()
    test_clock_seq_hi_variant()
    test_clock_seq_low()
    test_node()
    test_urn()
    test_uuid4()


--- stdlib_extensions/syscalls/__init__.mojo ---



--- stdlib_extensions/syscalls/c.mojo ---

# Types aliases
alias void = UInt8
alias char = UInt8
alias schar = Int8
alias uchar = UInt8
alias short = Int16
alias ushort = UInt16
alias int = Int32
alias uint = UInt32
alias long = Int64
alias ulong = UInt64
alias float = Float32
alias double = Float64
alias time_t = Int64

alias size_t = UInt64
alias ssize_t = Int

alias ptrdiff_t = Int64
alias intptr_t = Int64
alias uintptr_t = UInt64

# standard io
alias FD_STDIN: int = 0
alias FD_STDOUT: int = 1
alias FD_STDERR: int = 2

# --- ( error.h Constants )-----------------------------------------------------
alias SUCCESS = 0
alias EPERM = 1
alias ENOENT = 2
alias ESRCH = 3
alias EINTR = 4
alias EIO = 5
alias ENXIO = 6
alias E2BIG = 7
alias ENOEXEC = 8
alias EBADF = 9
alias ECHILD = 10
alias EAGAIN = 11
alias ENOMEM = 12
alias EACCES = 13
alias EFAULT = 14
alias ENOTBLK = 15
alias EBUSY = 16
alias EEXIST = 17
alias EXDEV = 18
alias ENODEV = 19
alias ENOTDIR = 20
alias EISDIR = 21
alias EINVAL = 22
alias ENFILE = 23
alias EMFILE = 24
alias ENOTTY = 25
alias ETXTBSY = 26
alias EFBIG = 27
alias ENOSPC = 28
alias ESPIPE = 29
alias EROFS = 30
alias EMLINK = 31
alias EPIPE = 32
alias EDOM = 33
alias ERANGE = 34
alias EWOULDBLOCK = EAGAIN

# random consts
alias GRND_NONBLOCK: UInt8 = 1

alias char_pointer = AnyPointer[char]


@value
struct Str:
    var vector: List[char]

    fn __init__(inout self, string: String):
        self.vector = List[char](capacity=len(string) + 1)
        for i in range(len(string)):
            self.vector.append(ord(string[i]))
        self.vector.append(0)

    fn __init__(inout self, size: Int):
        self.vector = List[char]()
        self.vector.resize(size + 1, 0)

    fn __len__(self) -> Int:
        for i in range(len(self.vector)):
            if self.vector[i] == 0:
                return i
        return -1

    fn to_string(self, size: Int) -> String:
        var result: String = ""
        for i in range(size):
            result += chr(self.vector[i].to_int())
        return result

    fn __enter__(owned self: Self) -> Self:
        return self^


--- stdlib_extensions/syscalls/clocks.mojo ---

from memory.unsafe import Pointer
from ..syscalls import c

alias _CLOCK_REALTIME = 0


@value
struct _CTimeSpec:
    var tv_sec: c.time_t
    var tv_nsec: c.long

    fn __init__(inout self):
        self.tv_sec = 0
        self.tv_nsec = 0


fn clock_gettime() -> _CTimeSpec:
    """Low-level call to the clock_gettime libc function."""

    var ts = _CTimeSpec()
    var ts_pointer = Pointer[_CTimeSpec].address_of(ts)

    var clockid_si32: c.int = _CLOCK_REALTIME

    external_call["clock_gettime", NoneType, c.int, Pointer[_CTimeSpec]](
        clockid_si32, ts_pointer
    )

    return ts


--- stdlib_extensions/syscalls/filesystem.mojo ---

from ..syscalls import c
from .._utils import custom_debug_assert


fn rmdir(pathname: String):
    with c.Str(pathname) as pathname_as_c_str:
        var output = external_call["rmdir", c.int, c.char_pointer](
            pathname_as_c_str.vector.data
        )
        if output == c.SUCCESS:
            return
        elif output == c.EACCES:
            custom_debug_assert(
                False,
                "Write access to the directory containing "
                + pathname
                + " was notallowed, or one of the directories in the path prefix of "
                + pathname
                + " did notallow search permission.",
            )
        elif output == c.EBUSY:
            custom_debug_assert(
                False,
                pathname
                + " is currently in use by the system or some process"
                "that prevents its removal.  On Linux, this means "
                + pathname
                + " is currently"
                "used as a mount point or is the root directory of the calling"
                " process.",
            )
        elif output == c.EFAULT:
            custom_debug_assert(
                False, pathname + " points outside your accessible address space."
            )
        elif output == c.EINVAL:
            custom_debug_assert(False, pathname + " has .  as last component.")
        elif output == c.ENOENT:
            custom_debug_assert(
                False,
                "A directory component in "
                + pathname
                + " does not exist or is adangling symbolic link.",
            )
        elif output == c.ENOMEM:
            custom_debug_assert(False, "Insufficient kernel memory was available.")
        elif output == c.ENOTDIR:
            custom_debug_assert(
                False,
                pathname
                + ", or a component used as a directory in "
                + pathname
                + ", is not, in fact, a directory.",
            )
        elif output == c.EPERM:
            custom_debug_assert(
                False,
                "The filesystem containing "
                + pathname
                + " does not support the removal of directories.",
            )
        elif output == c.EROFS:
            custom_debug_assert(
                False, pathname + " refers to a directory on a read-only filesystem."
            )
        else:
            custom_debug_assert(
                False, "rmdir failed with unknown error code: " + String(output)
            )


fn unlink(pathname: String):
    with c.Str(pathname) as pathname_as_c_str:
        var output = external_call["unlink", c.int, c.char_pointer](
            pathname_as_c_str.vector.data
        )
        if output == c.SUCCESS:
            return
        elif output == c.EACCES:
            custom_debug_assert(
                False,
                "Write permission is denied for the directory from which the file +"
                + pathname
                + " is to be removed, "
                "or the directory has the sticky bit set and you do not own the file.",
            )
        elif output == c.EBUSY:
            custom_debug_assert(
                False,
                "the file "
                + pathname
                + " is being used by the system in such a way thatit can’t be unlinked."
                " For example, you might see this error if the filename specifies the"
                " root directory or a mount point for a file system.",
            )
        elif output == c.ENOENT:
            custom_debug_assert(False, "he file " + pathname + " doesn’t exist.")
        elif output == c.EPERM:
            custom_debug_assert(
                False,
                (
                    "On some systems unlink cannot be used to delete the name of a"
                    " directory, or at least can only be used this way by a privileged"
                    " user."
                ),
            )
        elif output == c.EROFS:
            custom_debug_assert(
                False,
                pathname
                + " refers to a file on a read-only filesystem and thus cannot be"
                " removed.",
            )
        else:
            custom_debug_assert(
                False, "rmdir failed with unknown error code: " + String(output)
            )


fn read_string_from_fd(file_descriptor: c.int) -> String:
    alias buffer_size: Int = 2**13
    var buffer: c.Str
    with c.Str(size=buffer_size) as buffer:
        var read_count: c.ssize_t = external_call[
            "read", c.ssize_t, c.int, c.char_pointer, c.size_t
        ](file_descriptor, buffer.vector.data, buffer_size)
        if read_count == -1:
            custom_debug_assert(
                False, "Failed to read file descriptor" + String(file_descriptor)
            )

        # for stdin, stdout, stderr, we can do this approximation
        # normally we would decode to utf-8 as we go and check for \n, but we can't do that now because
        # we don't have easy to use utf-8 support.
        if read_count == buffer_size:
            custom_debug_assert(
                False,
                "You can only read up to "
                + String(buffer_size)
                + " bytes. "
                "Wait for UTF-8 support in Mojo for better handling of long inputs.",
            )

        return buffer.to_string(read_count)


fn read_from_stdin() -> String:
    return read_string_from_fd(c.FD_STDIN)


--- stdlib_extensions/syscalls/process.mojo ---

from ..syscalls import c


fn getpid() -> c.int:
    return external_call["getpid", c.int]()


--- stdlib_extensions/syscalls/random.mojo ---

from ..builtins import bytes
from ..syscalls import c
from .._utils import custom_debug_assert


fn getrandom(size: c.size_t) -> bytes:
    var result = bytes(size.to_int())
    var nb_bytes_written = external_call[
        "getrandom", c.ssize_t, AnyPointer[UInt8], c.size_t, c.uint
    ](result._vector.data, size, c.GRND_NONBLOCK.cast[DType.uint32]())
    if nb_bytes_written < 0:
        custom_debug_assert(False, "getrandom failed")
    if nb_bytes_written != result.__len__():
        custom_debug_assert(False, "getrandom didn't send enough bytes")
    return result


--- stdlib_extensions/time/__init__.mojo ---

from ..syscalls import clocks


fn time_ns() -> Int64:
    """Returns the number of nanoseconds since the epoch."""
    var time_struct = clocks.clock_gettime()

    return time_struct.tv_sec * 1_000_000_000 + time_struct.tv_nsec


fn time() -> Float64:
    """Returns the number of seconds since the epoch, in float."""
    var time_struct = clocks.clock_gettime()
    return (
        time_struct.tv_sec.cast[DType.float64]()
        + time_struct.tv_nsec.cast[DType.float64]() / 1_000_000_000
    )


@value
struct struct_time:
    var tm_year: Int
    var tm_mon: Int
    var tm_mday: Int
    var tm_hour: Int
    var tm_min: Int
    var tm_sec: Int
    var tm_wday: Int
    var tm_yday: Int
    var tm_isdst: Int

    fn __init__(inout self, values: Tuple[Int, Int, Int, Int, Int, Int, Int, Int, Int]):
        self.tm_year = values.get[0, Int]()
        self.tm_mon = values.get[1, Int]()
        self.tm_mday = values.get[2, Int]()
        self.tm_hour = values.get[3, Int]()
        self.tm_min = values.get[4, Int]()
        self.tm_sec = values.get[5, Int]()
        self.tm_wday = values.get[6, Int]()
        self.tm_yday = values.get[7, Int]()
        self.tm_isdst = values.get[8, Int]()

    fn __getitem__(self, index: Int) -> Int:
        if index == 0:
            return self.tm_year
        elif index == 1:
            return self.tm_mon
        elif index == 2:
            return self.tm_mday
        elif index == 3:
            return self.tm_hour
        elif index == 4:
            return self.tm_min
        elif index == 5:
            return self.tm_sec
        elif index == 6:
            return self.tm_wday
        elif index == 7:
            return self.tm_yday
        elif index == 8:
            return self.tm_isdst
        else:
            # TODO raise an error here
            return 0


--- stdlib_extensions/uuid/__init__.mojo ---

from ._classes import (
    UUID,
    RESERVED_NCS,
    RFC_4122,
    RESERVED_MICROSOFT,
    RESERVED_FUTURE,
    uuid4,
)


--- stdlib_extensions/uuid/_classes.mojo ---

from ..builtins.string import replace, strip
from ..builtins import bytes as bytes_, to_bytes
from ..os import urandom

from utils.static_tuple import StaticTuple
from memory import stack_allocation

alias RESERVED_NCS = "reserved for NCS compatibility"
alias RFC_4122 = "specified in RFC 4122"
alias RESERVED_MICROSOFT = "reserved for Microsoft compatibility"
alias RESERVED_FUTURE = "reserved for future definition"

alias UUIDBytes = SIMD[DType.uint8, 16]


struct SafeUUID:
    alias safe = 0
    alias unsafe = -1
    alias unknown = -2  # this is normally None but we don't have traits


fn uint8_simd_to_int[simd_size: Int](x: SIMD[DType.uint8, simd_size]) -> Int:
    # a bitcast would be better but we don't have that yet it seems
    var output = 0
    for i in range(simd_size):
        output += x[i].to_int() * ((2**8) ** (simd_size - i - 1))
    return output


@always_inline
fn get_bit(x: SIMD[DType.uint8, 1], i: Int) -> Bool:
    return ((x >> i) & 1).cast[DType.bool]()


struct UUID(Stringable):
    var __bytes: UUIDBytes
    var is_safe: Int

    fn __init__(
        inout self,
        owned hex: String,
        version: Int = -1,
        is_safe: Int = SafeUUID.unknown,
    ):
        hex = replace(hex, "urn:", "")
        hex = replace(hex, "uuid:", "")
        hex = strip(hex, "{}")
        hex = replace(hex, "-", "")

        # TODO: enable erroring when it's allowed to raise at compile time
        # if len(hex) != 32:
        #     custom_debug_assert(False, "badly formed hexadecimal UUID string")
        self.__init__(bytes_.fromhex(hex), version, is_safe=is_safe)

    fn __init__(
        inout self,
        bytes: bytes_,
        version: Int = -1,
        is_safe: Int = SafeUUID.unknown,
    ):
        self.__bytes = UUIDBytes(
            bytes[0],
            bytes[1],
            bytes[2],
            bytes[3],
            bytes[4],
            bytes[5],
            bytes[6],
            bytes[7],
            bytes[8],
            bytes[9],
            bytes[10],
            bytes[11],
            bytes[12],
            bytes[13],
            bytes[14],
            bytes[15],
        )
        if version != -1:
            # we set the version
            self.__bytes[6] |= UInt8((version << 4) + 0)
            self.__bytes[6] &= UInt8((version << 4) + 0xF)

            # we set the variant to RFC 4122
            self.__bytes[8] |= UInt8(0x80)
            self.__bytes[8] &= UInt8(0xBF)
        self.is_safe = is_safe

    fn __eq__(self, other: UUID) -> Bool:
        return self.__bytes == other.__bytes

    fn __ne__(self, other: UUID) -> Bool:
        return self.__bytes != other.__bytes

    # TODO: Can we vectorize those methods?
    fn __lt__(self, other: UUID) -> Bool:
        for i in range(16):
            if self.__bytes[i] == other.__bytes[i]:
                continue
            return self.__bytes[i] < other.__bytes[i]
        return False

    fn __gt__(self, other: UUID) -> Bool:
        for i in range(16):
            if self.__bytes[i] == other.__bytes[i]:
                continue
            return self.__bytes[i] > other.__bytes[i]
        return False

    fn __le__(self, other: UUID) -> Bool:
        for i in range(16):
            if self.__bytes[i] == other.__bytes[i]:
                continue
            return self.__bytes[i] <= other.__bytes[i]
        return True

    fn __ge__(self, other: UUID) -> Bool:
        for i in range(16):
            if self.__bytes[i] == other.__bytes[i]:
                continue
            return self.__bytes[i] >= other.__bytes[i]
        return True

    fn __str__(self) -> String:
        var hex = self.bytes().hex()
        return (
            hex[:8]
            + "-"
            + hex[8:12]
            + "-"
            + hex[12:16]
            + "-"
            + hex[16:20]
            + "-"
            + hex[20:]
        )

    fn __repr__(self) -> String:
        return "UUID('" + self.__str__() + "')"

    fn bytes(self) -> bytes_:
        var output = bytes_()

        for i in range(16):
            output += to_bytes(self.__bytes[i].to_int())

        return output

    fn time_low(self) -> Int:
        return uint8_simd_to_int(self.__bytes.slice[4, offset=0]())

    fn time_mid(self) -> Int:
        return uint8_simd_to_int(self.__bytes.slice[2, offset=4]())

    fn time_hi_version(self) -> Int:
        return uint8_simd_to_int(self.__bytes.slice[2, offset=6]())

    fn clock_seq_hi_variant(self) -> Int:
        return uint8_simd_to_int(self.__bytes.slice[1, offset=8]())

    fn clock_seq_low(self) -> Int:
        return uint8_simd_to_int(self.__bytes.slice[1, offset=9]())

    fn node(self) -> Int:
        # trick because simd size can only be a power of 2
        var node_bytes = self.__bytes.slice[8, offset=8]()
        node_bytes[0] = 0
        node_bytes[1] = 0
        return uint8_simd_to_int(node_bytes)

    fn urn(self) -> String:
        return "urn:uuid:" + str(self)

    fn variant(self) -> String:
        if not (self.__bytes[8] & 0x80).to_int():
            return RESERVED_NCS
        elif not (self.__bytes[8] & 0x40).to_int():
            return RFC_4122
        elif not (self.__bytes[8] & 0x20).to_int():
            return RESERVED_MICROSOFT
        else:
            return RESERVED_FUTURE

    fn version(self) -> Int:
        # The version bits are only meaningful for RFC 4122 UUIDs.
        if self.variant() == RFC_4122:
            return (self.__bytes[6] >> 4).to_int()
        else:
            # we should actually return None here but we don't have traits/unions
            return -1


def uuid4() -> UUID:
    """Generate a random UUID."""
    return UUID(bytes=urandom(16), version=4)


# The following standard UUIDs are for use with uuid3() or uuid5().

alias NAMESPACE_DNS = UUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
alias NAMESPACE_URL = UUID("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
alias NAMESPACE_OID = UUID("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
alias NAMESPACE_X500 = UUID("6ba7b814-9dad-11d1-80b4-00c04fd430c8")




--- LICENSE ---

MIT License

Copyright (c) 2023 TrainCheck.ai

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


--- README.md ---

# Mojomics

Leveraging [Mojo](https://github.com/modularml/mojo), `Mojomics` aims to be a collection of Jupyter notebooks and resources to empower bioinformatics researchers with the tools and insights they need to accelerate their projects.

## Table of Contents
- [Notebooks](#notebooks)
- [License](#license)
- [Contributing](#contributing)
- [Contact](#contact)
- [Acknowledgements](#acknowledgements)


## Notebooks
Navigate to the `notebooks` directory to access the available notebooks:

- `hamming`: Explore the Hamming distance algorithm for measuring the similarity between two sequences.
- `seqAlign`: Dive into sequence alignment techniques to find the optimal alignment between pairs of sequences.

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Contributing

We welcome contributions! Please create an issue or open a pull request.

## Contact

- GitHub Profile: [mattfaltyn](https://github.com/mattfaltyn)
- Email: matt@traincheck.ai

## Acknowledgements

- Special thanks to the bioinformatics community for the inspiration and support in creating Mojomics.
- Additional resources and references used in creating these notebooks can be found in the respective notebooks.

---



URL: https://www.modular.com/

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

80K+ developers building with MAX

# A high-performance generative AI framework

MAX replaces and simplifies your AI inference stack, maximizing performance and reducing cloud costs

## Achieve state of the art NVIDIAÂ GPU performance

Unlock state of the art latency and throughput without writing low-level CUDA code.

### Deploy MAX on GPUs

## Optimize your existing PyTorch & ONNX models

Migrate seamlessly without rewriting your AI models and pipelines on a unified AI stack.

## Use Mojo to supercharge your AIÂ applications

Extend your PythonÂ code with high-performance Mojo, a new programming language with the expressiveness of Python and the performance of C.

## Develop locally, deploy globally to any cloud.

Develop your AI applications locally and package and deploy across any cloud provider, on CPUs and GPUs, without having to change your code.

## Run Gen AI locally underÂ 3Â minutes

Download MAX first to run an LLM

- Run Llama3-8B in your CLIGet started

- Run Llama2-7B in your CLIGet started

- Run Replit-3B in your CLIGet started

Run Llama3-8B in your CLI

Run Llama2-7B in your CLI

Run Replit-3B in your CLI

## Free Forever

The MAXÂ framework is a free and open platform for you to develop and deploy AI inference workloads.

A new framework for Gen AI, and the best way to deploy PyTorch

Development tools for accelerated compute on GPUs and CPUs, built from the ground up for GenAI, but compatible with today.

## Developer Approved ð

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

## Get started now

- Getting started guideStep by step walkthrough on how to get started

- Browse examplesRun a project locally in just 2min

- Connect with buildersJoin over 22,000 developers

Getting started guide

Step by step walkthrough on how to get started

Browse examples

Run a project locally in just 2min

Connect with builders

Join over 22,000 developers

Download for your platformÂ now

- 

- 

- 

View Pricing

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

MAX on GPU waitingÂ list

Be the first to get lightning fast inference speed on your GPUs. Be the envy of all your competitors and lower your compute spend.

================================================================================

URL: https://www.modular.com/max

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# MAX empowers you to own &Â control your AI

A new framework for Gen AI, and the best way to deploy PyTorch

## Unparalleled GenAI performance

- Llama3 - Optimized pipelinesView on Github





Llama3.pyllama 3 ../run-pipeline.ð¥ llama3 --max-tokens 200 --prompt "Why 
the sky is blue?" --quantization-encoding q4_0

Loading tokenizer...
Building model...
Compiling...
Executing...

The sky appears blue due to a phenomenon called Rayleigh scattering...

Prompt size: 10
Output size: 190

- Deploy LLMs with a single commandCheckout MAX Builds

### Llama3 - Optimized pipelines

llama 3 ../run-pipeline.ð¥ llama3 --max-tokens 200 --prompt "Why 
the sky is blue?" --quantization-encoding q4_0

Loading tokenizer...
Building model...
Compiling...
Executing...

The sky appears blue due to a phenomenon called Rayleigh scattering...

Prompt size: 10
Output size: 190

## Deploy LLMs with a single command

## The best way to deploy PyTorch

- SOTA performance in just 3 lines of codeDrop in your PyTorch or ONNX models and get an instant boost in performance with our next generation inference runtime for CPUs and GPUsSee for yourself

## SOTA performance in just 3 lines of code

Drop in your PyTorch or ONNX models and get an instant boost in performance with our next generation inference runtime for CPUs and GPUs

## Compatible with what you use today

- Supports all your use cases and existing toolsUse the MAX APIs to build, optimize and deploy from one model to more complex GenAI pipelines on CPUs or GPUs.Supported model formats

## Supports all your use cases and existing tools

Use the MAX APIs to build, optimize and deploy from one model to more complex GenAI pipelines on CPUs or GPUs.

## Build locally. Deploy easily across hardware in the cloud

- Compute Abstraction for AIBuild your AI applications, package and deploy across CPUs and GPUs platforms including Apple, ARM, Intel, AMD and NVIDIA without code changes.Supported hardware

- Accelerate your time to marketÂ with MAXÂ onÂ AWSGet help from the experts using a production gradeÂ managed service on AWSLearn more

## Compute Abstraction for AI

Build your AI applications, package and deploy across CPUs and GPUs platforms including Apple, ARM, Intel, AMD and NVIDIA without code changes.

## Accelerate your time to marketÂ with MAXÂ onÂ AWS

Get help from the experts using a production gradeÂ managed service on AWS

## Develop with Python, Â ExtendÂ withÂ Mojoð¥

- Use what you know with Python APIs in MAXUse our Python integration to interop with your existing workloads and offloads onto MAX where it mattersUsing Python with MAX

- Learn how to scale your AI with MojoNo need to learn C and CUDA, use Mojo the easiest way to program CPUs and GPUs.Take a tour of Mojoð¥

## Use what you know with Python APIs in MAX

Use our Python integration to interop with your existing workloads and offloads onto MAX where it matters

## Learn how to scale your AI with Mojo

No need to learn C and CUDA, use Mojo the easiest way to program CPUs and GPUs.

## What developers are saying about MAX

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

âThe Community is incredible and so supportive. Itâs awesome to be part of.â

benny.n

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

âThe Community is incredible and so supportive. Itâs awesome to be part of.â

benny.n

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

âThe Community is incredible and so supportive. Itâs awesome to be part of.â

benny.n

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

âThe Community is incredible and so supportive. Itâs awesome to be part of.â

benny.n

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

## Get started now

- Getting started guideStep by step walkthrough on how to get started

- Browse examplesRun a project locally in just 2min

- Connect with buildersJoin over 22,000 developers

Getting started guide

Step by step walkthrough on how to get started

Browse examples

Run a project locally in just 2min

Connect with builders

Join over 22,000 developers

Download for your platformÂ now

- 

- 

- 

View Pricing

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/mojo

MAX on GPU waitingÂ list

Be the first to get lightning fast inference speed on your GPUs. Be the envy of all your competitors and lower your compute spend.

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# The expressiveness of Python, â¨with the performance of C

Mojo installs as part of MAX

- 175kMojo Developers

- 23kStars on Github

- 22kCommunity members

175k

Mojo Developers

23k

Stars on Github

22k

Community members

## Mojo has powerful &Â easyÂ to useÂ features

Leverage types for better performance and error checking.

def sort(v: ArraySlice[Int]):
  for i in range(len(v)):
    for j in range(len(v) - i - 1):
      if v[j] > v[j + 1]:
        swap(v[j], v[j + 1])

struct MyPair:
  var first: Int
  var second: F32
  
  def __init__(self, first: Int, second: F32):
    self.first = first
    self.second = second

def reorder_and_process(owned x: HugeArray):
  sort(x)	# Update in place
  
  give_away(x^)	# Transfer ownership
  
  print(x[0])	# Error: âxâ moved away!

def exp[dt: DType, elts: Int]
    (x: SIMD[dt, elts]) -> SIMD[dt, elts]:
  x = clamp(x, -88.3762626647, 88.37626266)
  k = floor(x * INV_LN2 + 0.5)
  r = k * NEG_LN2 + x
  return ldexp(_exp_taylor(r), k)

def exp_buffer[dt: DType](data: ArraySlice[dt]):

  # Search for the best vector length
  alias vector_len = autotune(1, 4, 8, 16, 32)
  
  # Use it as the vectorization length
  vectorize[exp[dt, vector_len]](data)

def sort(v: ArraySlice[Int]):
  for i in range(len(v)):
    for j in range(len(v) - i - 1):
      if v[j] > v[j + 1]:
        swap(v[j], v[j + 1])

struct MyPair:
  var first: Int
  var second: F32
  
  def __init__(self, first: Int, second: F32):
    self.first = first
    self.second = second

def reorder_and_process(owned x: HugeArray):
  sort(x)	# Update in place
  
  give_away(x^)	# Transfer ownership
  
  print(x[0])	# Error: âxâ moved away!

def exp[dt: DType, elts: Int]
    (x: SIMD[dt, elts]) -> SIMD[dt, elts]:
  x = clamp(x, -88.3762626647, 88.37626266)
  k = floor(x * INV_LN2 + 0.5)
  r = k * NEG_LN2 + x
  return ldexp(_exp_taylor(r), k)

def exp_buffer[dt: DType](data: ArraySlice[dt]):

  # Search for the best vector length
  alias vector_len = autotune(1, 4, 8, 16, 32)
  
  # Use it as the vectorization length
  vectorize[exp[dt, vector_len]](data)

- Progressive types

- Zero cost abstractions

- Ownership + borrow checker

- Portable parametric algorithms

- Language integrated auto-tuning

Progressive types

Zero cost abstractions

Ownership + borrow checker

Portable parametric algorithms

Language integrated auto-tuning

Progressive types

Zero cost abstractions

Ownership + borrow checker

Portable parametric algorithms

Language integrated auto-tuning

Extend Python or scale all the way down to the metal. Program the multitude of low-level AI hardware. No C++ or CUDA required.

Using Mojo with MAX unlocks the future of accelerated compute for the world

Development tools for accelerated compute on GPUs and CPUs, built from the ground up

## Mojoð¥ + MAX unlock incredible NVIDIA GPU performance

Mojo with MAX enables state of the art latency and throughput without writing low-level CUDA code.

### Deploy MAX on GPUs

## See what AI developers are building withÂ Mojoð¥

LLM.mojo

A implementation of Andrej Karpathy's llm.c to Mojo ð¥

Basalt

A machine learning framework in pure Mojo ð¥

Lightbug HTTP

A HTTP web framework written in pure Mojo ð¥

Endia

A dynamic Array library for Scientific Computingð¥

+300

more open source projects

## Developers love MojoÂ ð¥

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

Mojo  destroys Python in speed.  12x faster without even trying. The future is bright!

mytechnotalent

âThe Community is incredible and so supportive. Itâs awesome to be part of.â

benny.n

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ



âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

## Unlock Python performance

Utilize the full power of the hardware, including multiple cores, vector units, and exotic accelerator units, with the world's most advanced compiler and heterogenous runtime. Achieve performance on par with C++ and CUDA without the complexity.

- PythonSingle Thread Distribution

- Mojoð¥Parallel processing across multiple cores

### Python

Single Thread Distribution

### Mojoð¥

Parallel processing across multiple cores

## Mojo enables systems programming features so you can process information exponentiallyÂ faster.

0.03s

68,000x

* Algorithm

Mandelbrot

Instance

AWS C1.xlarge

Intel Xeon

Read our 68,000x blog post

## Access the entire PythonÂ ecosystem

Experience true interoperability with the Python ecosystem. Seamlessly intermix arbitrary libraries like Numpy and Matplotlib and your custom code with Mojo.

- 

- Mojo ð¥

Mojo ð¥

Python

def make_plot(m: Matrix):
  plt = Python.import_module("matplotlib.pyplot")
  fig = plt.figure(1, [10, 10 * yn // xn], 64)
  ax = fig.add_axes([0.0, 0.0, 1.0, 1.0], False, 1)
  plt.imshow(image)
  plt.show()

make_plot(compute_mandelbrot())

## Upgrade your models toÂ fullÂ pipelines with MAX

Easily extend your models with pre and post- processing operations, or replace operations with custom ones. Take advantage of kernel fusion, graph rewrites, shape functions, and more.

## Get started now

- Getting started guideStep by step walkthrough on how to get started

- Browse examplesRun a project locally in just 2min

- Connect with buildersJoin over 22,000 developers

Getting started guide

Step by step walkthrough on how to get started

Browse examples

Run a project locally in just 2min

Connect with builders

Join over 22,000 developers

Download for your platformÂ now

- 

- 

- 

View pricing

Join the community

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/pricing

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# AI infrastructure for everyone

We are, and will always be, developer first. â¨Our license empowers everyone to build incredible AI.

- FREE FOREVER



MAX is available FREE for everyone to self-manage.What do I get with MAX?


SoTA inference performance for LLMs, PyTorch, and ONNX models


Run AIÂ models and pipelines on any CPUs or GPUs


Deploy MAX yourself on-prem or on any cloud provider


Community support through Discord and Github


MAXÂ &Â Mojo Community LicenseDownload Now

- SoTA inference performance for LLMs, PyTorch, and ONNX models

- Run AIÂ models and pipelines on any CPUs or GPUs

- Deploy MAX yourself on-prem or on any cloud provider

- Community support through Discord and Github

- MAXÂ &Â Mojo Community License

- PAYÂ ASÂ YOUÂ GO













Support the largest deployments needed by your enterprise.What do I get with MAX Enterprise?


SLA support with guaranteed response time.


Dedicated Slack channel and account manager.


Access to the worldâs best AI engineering team.


Prioritized feature requests and early access to our managed services AIÂ platform.


Enterprise LicenseContact Sales

- SLA support with guaranteed response time.

- Dedicated Slack channel and account manager.

- Access to the worldâs best AI engineering team.

- Prioritized feature requests and early access to our managed services AIÂ platform.

- Enterprise License

- Still have questions about MAX licenses?Talk to us

FREE FOREVER

MAX is available FREE for everyone to self-manage.

What do I get with MAX?

- SoTA inference performance for LLMs, PyTorch, and ONNX models

- Run AIÂ models and pipelines on any CPUs or GPUs

- Deploy MAX yourself on-prem or on any cloud provider

- Community support through Discord and Github

- MAXÂ &Â Mojo Community License

SoTA inference performance for LLMs, PyTorch, and ONNX models

Run AIÂ models and pipelines on any CPUs or GPUs

Deploy MAX yourself on-prem or on any cloud provider

Community support through Discord and Github

MAXÂ &Â Mojo Community License

PAYÂ ASÂ YOUÂ GO

Support the largest deployments needed by your enterprise.

What do I get with MAX Enterprise?

- SLA support with guaranteed response time.

- Dedicated Slack channel and account manager.

- Access to the worldâs best AI engineering team.

- Prioritized feature requests and early access to our managed services AIÂ platform.

- Enterprise License

SLA support with guaranteed response time.

Dedicated Slack channel and account manager.

Access to the worldâs best AI engineering team.

Prioritized feature requests and early access to our managed services AIÂ platform.

Enterprise License

Still have questions about MAX licenses?

## Frequently asked questions

## General

What is MAX?

MAX is a free and permissive AI inference framework that enables developers and enterprises to develop and deploy AI inference workloads on any hardware type, into any type of environment (including into production). You can read more about MAX here.

What is Mojo?

Mojo is a new programming language that has the expressiveness of Python, with the performance of C. Mojo is a member of the Python family and powers critical parts of our infrastructure. Mojo is completely optional to use, as MAX supports Python, C++ and other languages natively. You can read more about Mojo here.

What is MAX Enterprise?

MAX Enterprise is a cloud platform we are developing for enterprises who seek managed services and enterprise support from us for AI infrastructure.

What kind of licenses are MAX and Mojo governed by?

MAX and Mojo are governed by a modified Business Source License which we call the MAX & Mojo Community License. We have adopted this license to ensure that we provide maximum flexibility to the world to utilize our infrastructure while balancing our ability to commercialize our infrastructure for the long term.

â

Modular open sources a lot of its technology, including the Mojo Standard Library, implementations of deep learning models like Llama 3 and more. This code is available on GitHub and it is licensed under Apache 2 with LLVM Exceptions, and includes contributions we receive from the community. These contributions are incorporated in MAX and Mojo and their usage and distribution (as part of MAX and Mojo) are covered by the broader MAX & Mojo Community License. We strongly believe in open-source software and are committed to continually opening our platform over time.

Why did you choose these licenses for MAX and Mojo?

For MAX and Mojo, we took the modified BSL approach because it allows enterprises, developers, and our rapidly expanding community to copy, modify and redistribute their applications under a wide range of conditions, including use in production. There are many other licenses like AGPL and SSPL that impose copyleft requirements and are problematic for many use cases. We believe our licensing approach strikes the right balance for both MAX and Mojo and only restricts use for a narrow range of use cases that are competitive with Modularâs business activities.

â

For our Github repositories and open sourced code, along with contributions from our community - we strongly believe in the Apache 2 with LLVM Exceptions, as it provides legal protection to users and contributors of the software. We outline more thoughts in this blog post.

What kinds of applications can I build under the MAX & Mojo Community License?

You are free to build, extend and develop anything on top of MAX and Mojo, including products and services that you want to monetize or that you can derive an income from (i.e. content on YouTube, Twitch, etc.), and you can put them in production. You can even build products that are competitive to Modular, as long as you aren't commercializing them. We have a specific provision defined for what we deem a "Competitive Activity" that captures what you canât do in our License.

What is a Competitive Activity?

To ensure that we can drive a commercial model for our business, we define a Competitive Activity as building a commercial, publicly available product or service whose primary purpose is to provide AI infrastructure services to third parties, or associated paid support for this purpose.Â

â

Outside of this definition, you are free to build whatever you want with MAX and Mojo and integrate it into your products or services. For example, if you are building a startup whose primary purpose is AI code generation, or search, or health agents, you can use MAX and Mojo freely as part of your startup. Similarly, if you want to educate folks on YouTube on MAX and Mojo, you can do that freely relevant to our Trademark Policy. Conversely, if you want to build a startup whose primary purpose is to provide managed artificial intelligence services to third parties in a cloud hosted platform using MAX and Mojo for profit, its primary purpose overlaps our definition, and that would be deemed a Competitive Activity, which you cannot do without written permission.

â

We have done this to ensure existing organizations whose commercial models serve the primary purpose of providing AI infrastructure services canât simply take our infrastructure and offer it themselves without our permission. We are building MAX Enterprise for this purpose, and as the creators, only we can provide the world's best enterprise and developer support accordingly. If youâd like to discuss licensing or commercialization of MAX and Mojo in a way that seems competitive, please contact our Sales Team â weâre very friendly.

What if Modular expands its products and services in the future?

If you want to build software that is not competitive today, but becomes competitive in the future, the License protects you â it will not be considered competitive. You will be free to continue to develop, iterate and extend your application.

How much do I have to pay to use MAX?

MAX is free to utilize and deploy in research and production environments on your own infrastructure, as long as your use case is not a Competitive Activity.

Can I host MAX myself for production?

Yes, you are free to use MAX to build any production applications you want, including deploying MAX yourself, as long as your use case is not a Competitive Activity.

Can I embed the MAX framework into software I distribute?

Yes, just make sure your application is not a Competitive Activity and follows the terms of the License.

Are there any scale limitations in the use of the MAX framework?

No limitations, you may scale MAX to any workload and size you need for supported hardware.

Will NVIDIA GPU support be licensed the same way?

Yes, we aim to release MAX support for NVIDIA GPUs in fall 2024 and that will use the same approach as the current CPU support.

Can I get enterprise support for the MAX?

Yes, that is available under our MAX Enterprise version of MAX. You can learn more on our Enterprise page, or contact our Sales Team.

Will MAX Enterprise offer managed cloud services?

Yes, we are building a managed AI cloud platform for MAX Enterprise with early design partners for AI infrastructure services. Please Contact Sales if you are interested in becoming an early partner.

What is the boundary between Mojo and MAX in terms of licensing?

MAX and Mojo today are licensed under the same License as indicated above. You are free to use them to build anything you want, including in production, as long as you arenât building something that is a Competitive Activity. Mojo is a very horizontal programming language and weâd love to see it used to build many use-cases that are not currently solved with other programming languages.

Can I use Mojo in production environments?

You are free to build, extend and develop anything with Mojo as long as it's not a Competitive Activity within the scope of our License.

Why not package Mojo separately from MAX?

We are open to distributing Mojo without MAX in the future, but today MAX is deeply coupled to Mojo and distributing them together as a single package allows us to move faster. We are already having trouble keeping up with community activity, and are optimizing our ability to move fast and build the worldâs best platform for heterogeneous compute. We also want to learn and evolve with our community, and learn how Mojo is being utilized with MAX.

Why will GPU support require MAX?

Accelerators require runtime components and orchestration logic that falls into the domain of MAX, not Mojo. The best way to use Mojo with GPUs and other accelerated computing is inside the MAX Framework. The Graph APIs and other extensibility APIs allow you to run Mojo code directly on GPUs and other hardware.

Will Mojo ever be fully open source?

Yes. We remain strong believers in the importance of open-source software to accelerating innovation and are deeply committed to progressively make source code of Mojo fully available over time.

What is the Apache 2 License with LLVM exceptions about?

Modular open sources a lot of its technology, including the Mojo Standard Library, implementations of deep learning models like Llama 3 and more. This code is available on GitHub and it is licensed under Apache 2 with LLVM Exceptions, and includes contributions we receive from the community.

â

We strongly believe in the Apache 2 with LLVM Exceptions, as it provides legal protection to users and contributors of the software, and we outline more thoughts in this blog post. All contributions are incorporated in MAX and Mojo and their usage and distribution (as part of MAX and Mojo) are covered by the broader MAX & Mojo Community License.

## Get started now

- Getting started guideStep by step walkthrough on how to get started

- Browse examplesRun a project locally in just 2min

- Connect with buildersJoin over 22,000 developers

Getting started guide

Step by step walkthrough on how to get started

Browse examples

Run a project locally in just 2min

Connect with builders

Join over 22,000 developers

Download for your platformÂ now

- 

- 

- 

What is MAX?

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog/take-control-of-your-ai

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

July 9, 2024

# Take control of your AI

Modular Team

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

Today, enterprises are trying to adopt AI at an unprecedented pace â in fact, the latest research offered by Bain & Company suggests:

â

While the benefits of AI are increasingly clear, a notable trend is also starting to emerge: enterprise teams are now assessing how to own and control their AI systems rather than relying on third-party providers like OpenAI.

### Bringing AI in-house

As the race to deploy AI into enterprise exploded rapidly, many enterprises tried to adopt AI as fast as possible without considering the broader impacts to their organizations and the impact on their employees, products, and customers. Now, as enterprises see their AI efforts maturing, and with more proof of concepts (POCs) moving to production, they are increasingly asking what they need to do to scale AI inside their organizations.

We regularly talk to enterprises about their AI needs, and here is a list of what enterprise customers tell us is important to them:

#### Customization and flexibility

Product and engineering teams need the ability to customize their AI deployments â ensuring insight into data processing methods, model formats, AI pipelines, training methods, model serving, production monitoring and more. This level of control and customization is unattainable with off-the-shelf, or with third-party AI solutions. Product and engineering teams must innovate without constraints, deploying AI and customizing the implementation for your organizational needs.

#### Intellectual property protection

You wouldnât trust your most critical IP to a third party, and AI will increasingly become some of your most important IP. Over time, enterprise teams will create an increasing number of proprietary algorithms and approaches. By controlling enterprise AI, you can own and scale this important intellectual property, maintaining or growing your enterprise's competitive edge.

#### Innovation and agility

The ability to experiment, iterate, and adapt quickly is crucial for determining where AI can help your organization. Owning your AI systems ensures that you can foster an environment of continuous innovation, where organizational teams can explore new applications and rapidly respond to market changes. Third-party solutions can be short-lived, as they constrain innovation to third-party update cycles and schedules. Empowering your organization to own its AI enables your enterprise to drive innovation forward quickly.

#### Resource allocation & cost efficiency

While establishing in-house AI capabilities requires upfront investment, rapidly deploying AI across an enterprise is expensive. Third-party services often come with recurring fees and scaling costs that can escalate unpredictably â particularly on a token or context basis. Owning your AI future means that you can manage cost and scale AI within resource allocation and budgetary control standards. Escalating costs from a lack of control of your AI infrastructure systems often means months or years of challenges as you race to reduce your third-party AI dependency.

#### Data privacy and security

Safeguarding data privacy and security is paramount. AI systems thrive on vast datasets, often containing sensitive and proprietary information. By maintaining control over your AI infrastructure, your teams can implement robust security measures tailored to their unique needs. This control mitigates the risk of data breaches and unauthorized access â issues that can be magnified when outsourcing AI solutions. Ensuring that you control AI in-house allows your product and engineering teams to build a fortress around their data, ensuring its integrity and confidentiality.

#### Compliance and regulatory alignment

For enterprises in finance, healthcare, and telecommunications - there are real and stringent regulations governing data handling and processing. In these sectors, enterprise teams face the difficult challenge of ensuring AI systems not only scale correctly, but also comply with strict state and federal legal requirements. By owning your AI, you can directly implement and monitor compliance measures - reducing the risk of regulatory breaches and associated penalties.

#### Data quality and bias mitigation

AI systems are deeply reliant on data and its quality. Enterprises with control over their AI infrastructure have greater oversight of data preprocessing and cleaning procedures, ensuring high data integrity standards which improves AI model quality. Further, this enables active identification and mitigation of biases in AI models, delivering fairer and more accurate outcomes. The lack of transparency into third-party services, indicates that you do not have deep insight into what is actually being done with your data.

#### Integration challenges

Many enterprises operate within complex IT ecosystems that blend legacy systems, databases, and applications with modern code bases. A lack of control over AI workloads means its can be hard to scale AI into these environments. Controlling your AI deployment infrastructure enables you minimize integration challenges, building a more cohesive technology stack.

#### Building internal expertise

Without question, growing and scaling your AI talent ensures you can constantly scale with the latest AI breakthroughs. Building an AI-first enterprise requires deep investment in product and engineering teams and a commitment to developing expertise to scale AI systems. This will only make your organization more self-sufficient and resilient. All your teams must be AI teams, and fostering a strong internal culture of innovation and talent development ensures you can own your AI future.

### How does MAX help?

At Modular, we have been at the forefront of building infrastructure that hands back ownership and control to enterprises seeking to own their AI future. As leaders in AI infrastructure who helped build the infrastructure that shaped todayâs AI industry, we have taken a new approach to help developers and enterprises answer the critical question: What if deploying AI workloads into production was so simple you could do it yourself?

Thatâs why we built MAX - the Modular Accelerated Xecution Platform.

MAX gives you everything you need to deploy low-latency, high-throughput AI applications into production with minimal effort. More specifically, it provides the following:

#### Industry standard protocols

MAX works with the industry-standard APIs and protocols that have become ubiquitous with the mass adoption of Generative AI and LLMs, including the OpenAIâs completion and chat APIs. Importantly, this significantly reduces the cost of migrating your application code to your AI systems, giving you the flexibility to choose the best engine for your needs.

#### Portable, performant model execution

MAX provides a state-of-the-art AI compiler and runtime that optimizes the latency and throughput of popular open source AI models like Llama3-8B and Gemma2 across a wide range of AI hardware, from local laptops to common cloud instances. MAX enables you to seamlessly move the same model, without code changes, across a wide range of CPU architecturesâIntel, AMD, ARMâand GPUs, allowing you to take advantage of the breadth and depth of different cloud instances at the best price, and always get the best inference cost-performance ratio across cloud environments.

#### Compatible with what you already use

For most organizations experimenting with GenAI and LLMs, this isnât their first foray into AI. Many of them already have traditional AI models scaled in production. These organizations have been forced to split their infrastructure efforts, despite already having well established standard infrastructure. MAX standardizes this existing infrastructure and extends it to GenAI and LLMs, replacing the parts of the stack that matter most. MAX is compatible with all PyTorch and ONNX models, including object detection, recommenders, and much more. It integrates with industry standard technologies such as Triton Inference Server, Docker, Prometheus, and Grafana.

#### Composable abstractions that allow for extensibility

For Enterprises looking to get their hands dirty and adapt GenAI and LLMs to their needs, MAX provides clean, composable abstractions, including the Serve, Engine, Drive, Graph, and Extensibility APIs. These abstractions enable users to go beyond stock LLMs and build a moat for their business in areas of AI that have yet to become mainstream.

#### Deploy to your own VPC or data center

Finally, MAX is deployable into any cloud or on-prem environment. Enterprises maintain data sovereignty, privacy, and control over how their data is used and how their AI services are scaled. When theyâre ready, users can adopt the Enterprise Edition and get world-class support from the team that scaled Googleâs AI.

### MAX is free! Download now

The decision for an enterprise to control its own AI is more than a strategic one â it's a call to action for the entire organization to become AI first. It signifies a commitment to data security, customization, and innovation. By taking charge of AI, enterprises empower their entire organization to craft sophisticated solutions that drive long-term success. This approach protects valuable data and intellectual property. It positions their team at the forefront of technological advancementâready and able to drive innovation and seize the AI opportunities that lie ahead.

By adopting MAX in your enterprise, you can drive this innovation across your entire organization, ensuring that you are in a position to control for your AI future. Learn more here, and contact us if you need help.

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Product

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Modular Team

Company

Our mission is to have real, positive impact in the world by reinventing the way AI technology is developed and deployed into production with a next-generation developer platform.



================================================================================

URL: https://www.modular.com/blog

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# Blog

## Featured

ð¨

NEW

ð¥

POPULAR

Developer

Deep dive into ownership in Mojo

June 4, 2024

ð¨

NEW

ð¥

POPULAR

Developer

Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

June 17, 2024

## Latest

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

ð¨

NEW

ð¥

POPULAR

Engineering

Debugging in Mojoð¥

July 16, 2024

ð¨

NEW

ð¥

POPULAR

Product

Bring your own PyTorch model

July 9, 2024

ð¨

NEW

ð¥

POPULAR

Product

Take control of your AI

July 9, 2024

ð¨

NEW

ð¥

POPULAR

Product

Develop locally, deploy globally

July 9, 2024

ð¨

NEW

ð¥

POPULAR

Developer

A brief guide to the Mojo n-body example

July 3, 2024

## Popular Articles

ð¨

NEW

ð¥

POPULAR

Engineering

Debugging in Mojoð¥

July 16, 2024

ð¨

NEW

ð¥

POPULAR

Product

Develop locally, deploy globally

July 9, 2024

ð¨

NEW

ð¥

POPULAR

Developer

A brief guide to the Mojo n-body example

July 3, 2024

ð¨

NEW

ð¥

POPULAR

Developer

Deep dive into ownership in Mojo

June 4, 2024

ð¨

NEW

ð¥

POPULAR

Developer

What ownership is really about:  a mental model approach

May 29, 2024

ð¨

NEW

ð¥

POPULAR

Developer

Fastâ¡k-means clustering in Mojoð¥: a guide to porting Python to Mojoð¥ for accelerated k-means clustering

May 20, 2024

## For Developers

ð¨

NEW

ð¥

POPULAR

Developer

A brief guide to the Mojo n-body example

July 3, 2024

ð¨

NEW

ð¥

POPULAR

Developer

What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

June 25, 2024

ð¨

NEW

ð¥

POPULAR

Developer

Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

June 17, 2024

ð¨

NEW

ð¥

POPULAR

Developer

Deep dive into ownership in Mojo

June 4, 2024

ð¨

NEW

ð¥

POPULAR

Developer

What ownership is really about:  a mental model approach

May 29, 2024

ð¨

NEW

ð¥

POPULAR

Developer

Fastâ¡k-means clustering in Mojoð¥: a guide to porting Python to Mojoð¥ for accelerated k-means clustering

May 20, 2024

## Whatâs new with MAX?

ð¨

NEW

ð¥

POPULAR

Product

Bring your own PyTorch model

July 9, 2024

ð¨

NEW

ð¥

POPULAR

Product

Take control of your AI

July 9, 2024

ð¨

NEW

ð¥

POPULAR

Product

Develop locally, deploy globally

July 9, 2024

ð¨

NEW

ð¥

POPULAR

Product

MAX 24.4 - Introducing quantization APIs and MAX on macOS

June 7, 2024

ð¨

NEW

ð¥

POPULAR

Product

MAX 24.3 - Introducing MAX Engine Extensibility

May 2, 2024

ð¨

NEW

ð¥

POPULAR

Product

MAX 24.2 is Here! Whatâs New?

March 28, 2024

## Get started now

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/modverse

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# Modverse Newsletter

## Latest Issues

ð¨

NEW

Modverse Weekly - Issue 41

August 16, 2024

ð¨

NEW

Modverse Weekly - Issue 40

August 1, 2024

## Sign up for our newsletter, Modverse

Get all our latest news, announcements and updates delivered directly to your inbox. Unsubscribe at anytime.

Thank you for your submission.

Your report has been received and is being reviewed by the Sales team. A member from our team will reach out to you shortly.

Thank you,

Modular Sales Team

# All Modverse Issues (X)

ð¨

NEW

### Modverse Weekly - Issue 41

The Mojo Community Meeting has been renamed to the MAX + Mojo Community Meeting. We explained how we combined Mojo and MAX, and updated the license for both to ensure you can be confident building free (and open source) technologies, as well as using Mojo and MAX for your own commercial projects.

August 16, 2024

/

Jack Clayton

,Â

ð¨

NEW

### Modverse Weekly - Issue 40

Last week we began alpha testing our new magic CLIÂ for installing Mojo and MAXÂ through the Conda ecosystem. It allows you to pin Mojo/MAXÂ to different versions, and add Python dependencies so you can reliably share your Mojo and MAXÂ projects. If you want to try it out before release, you can find the doc for installing it here.

August 1, 2024

/

Jack Clayton

,Â

ð¨

NEW

### Modverse Weekly - Issue 39

Check out the new section for nightly updates. If you're not keeping track on the #nightly Discord channel, you can catch up on everything new for the last week in this newsletter.

July 9, 2024

/

Jack Clayton

,Â

ð¨

NEW

### Modverse Weekly - Issue 38

The 24.4 release introduced GGUFÂ support and quantisation for models such as Llama3 and many new features for Mojo. With that behind us Modular is now working hard towards the next release. To try out the new features press the Nightly tab on the MAXÂ installation guide. There's an active community in the #nightly Discord channel to discuss the upcoming features with, and Modular staff are there to answer questions and respond to your feedback!

June 28, 2024

/

Jack Clayton

,Â

ð¨

NEW

### Modverse Weekly - Issue 37

We hosted the third Mojo Community Meeting this week, with great demonstrations from the community on constraints and the Mojo HTTP serving library lightbug. We also previewed our upcoming AIÂ pipeline serving feature with llama3, using a GUI to interact with it. And ended by answering community questions on async, and when Python will be able to call Mojo.

June 20, 2024

/

Jack Clayton

,Â

ð¨

NEW

### Modverse Weekly - Issue 36

This week we released Mojoð¥ and MAXâ¡ï¸Â 24.4, there is too much in the release to cover here! Check out the launch blog, the Mojo changelog, and the MAXÂ changelog.

June 7, 2024

/

Jack Clayton

,Â

ð¨

NEW

### Modverse Weekly - Issue 35

This week we held the first ever Mojo community meeting! Many topics were discussed, including priorities, diving into specific features like async, and topics set on the agenda by the wider Mojo community. Next week we'll be hearing from community members about the awesome projects they've been working on. Make sure to add the next community meeting to your calendar.

May 23, 2024

/

Jack Clayton

,Â

ð¨

NEW

### Modverse Weekly - Issue 34

This week we launched MAXâ¡ï¸ nightlies with preview support for macOS and MAXÂ Serving! You can now find details about how to install it on modul.ar/get-started, press the Nightly button on the install guide if you want to be on the bleeding edge. We look forward to your feedback, join the Discord and leave comments on the #nightly channel.

May 17, 2024

/

Jack Clayton

,Â

ð¨

NEW

### Modverse Weekly - Issue 33

This week we hosted a livestream with many good questions from the community. People were most interested in macOS support for MAX, and GPUÂ support. These are both top priority at Modular so stay tuned! The standard library team were happy to announce that we've had 250 OSS contributions since open sourcing, we continue to be impressed and motivated by our active community!

May 10, 2024

/

Jack Clayton

,Â

ð¨

NEW

### Modverse Weekly - Issue 32

Yesterday marked Mojoð¥'s first birthday ð, launching last year on May 2, 2023. We launched with just an online Jupyter environment and have since released the SDK locally for macOS and Linux on arm64 and x86, and bought up our inference and serving stack MAXâ¡ï¸. Yesterday we released Mojo and MAX 24.3 to mark the occasion, along with the first community contributions, 32 of which made it into the official changelog. We're so excited to go on this journey out in the open with our amazing community, thank you to everyone who has been participating!

May 3, 2024

/

Jack Clayton

,Â

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/company/about

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# Build AI for anyone, anywhere.

## Our mission is to have real, positive impact in the world by reinventing the way AI technology is developed and deployed into production with a next-generation developer platform.

We believe that AI is a net positive force in the world and will help transform it for the better.

Our vision and mission are to reinvent AI infrastructure to advance humanity.

### We aim to push the whole world of AI forward, not just aÂ select few companies andÂ products.

After working for years at the worldâs largest technology companies, scaling the world's largest AI work loads, building the hardware that powers them, and deploying AI to billions of mobile phones and edge devices, we saw that fragmentation and technical complexity held back the impact to a privileged few.

We need to rethink the current AI systems and infrastructure from first principles to make it easy for anyone to leverage AI to solve the worldâs most critical problems with software that just works.

Modular and composable infrastructure that simplifies AI development and deployment is what the world needs.

Together, as Modulers, we are a team of the world's best AI infrastructure leaders who are reinventing and rebuilding accelerated compute for everyone.

### How it started

Chris Lattner and Tim Davis met at Google and felt AI was being held back by overly complex and fragmented infrastructure. Motivated by a desire to accelerate the impact of AI on the world by lifting the industry towards production-quality AI software, they founded Modular.

Leadership team

Chris Lattner

Co-Founder & CEO

Tim Davis

Co-Founder & President

Adin Scannell

Cloud Infrastructure Lead

Eric Johnson

Product Lead

Joe Pamer

Mojo Distinguished Engineering Lead

Kalor Lewis

VP, Finance

Mike Edwards

Operations Lead

Mostafa Hagog

Performance & Compiler Engineering Lead

Nick Kreeger

Framework Engineering Lead

## Backed by the best investors in AI

Our goal is as enormous as it is profound. Weâre building a different kind of company to achieve this.

We have assembled the best AI software and hardware leaders, and are systematically rebuilding the AI software stack from the ground up.

ð Weâre hiring!

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Chris Lattner

Co-Founder & CEO

Distinguished Leader who founded and scaled critical infrastructure including LLVM, Clang, MLIR, Cloud TPUs and the Swift programming language. Chris built AI and core systems at multiple world leading technology companies including Apple, Google, SiFive and Tesla.

clattner@modular.com

Tim Davis

Co-Founder & President

Repeat Entrepreneur and Product Leader. Tim helped build, found and scale large parts of Google's AI infrastructure at Google Brain and Core Systems from APIs (TensorFlow), Compilers (XLA & MLIR) and runtimes for server (CPU/GPU/TPU) and TF Lite (Mobile/Micro/Web), Android ML & NNAPI, large model infrastructure & OSS for billions of users and devices. Loves running, building and scaling products to help people, and the world.

tim@modular.com

Adin Scannell

Cloud Infrastructure Lead

As a hands-on technical leader, Adin is passionate about building products and technologies that challenge the status quo. He previously co-founded GridCentric which was acquired by Google, where he worked on problems at scale for nine years across Google and Google Cloud. He has spent the last two decades solving problems across the infrastructure stack â from hypervisors and operating systems to containers and security.



Eric Johnson

Product Lead

Product leader who has built and scaled AI applications and infrastructure. Eric led the TensorFlow API, Compiler, and Runtime teams at Google Brain and Core Systems, including the founding of TFRT and the productionization of JAX. He holds an MBA from Wharton and Computer Science MS from Penn and loves soccer, fitness, and the great outdoors.

eric@modular.com

Joe Pamer

Mojo Distinguished Engineering Lead

Joe spent the last 8 years at Meta, where he was focused on improving the companyâs AI and developer infrastructure stacks. Before that he served as Instagramâs CTO, overseeing much of Metaâs work on programming languages (mainly focused on Python, PHP, and Javascript), and co-founded its first applied ML team for infrastructure. Before joining Facebook, he helped design and develop the F#, TypeScript, and Swift programming languages, and contributed to many other developer technologies, such as Clang, Visual Studio, and VS Code.



Kalor Lewis

VP, Finance

Kalor is Modular's VP, Finance and leads all our Finance operations. Prior to Modular, Kalor was a VP, Finance at Fivetran where he was the first finance hire in 2018 and built out the companies entire finance function. Before Fivetran, Kalor was part of Palantir Technologies, where he scaled their strategic finance function.



Mike Edwards

Operations Lead

Mike has spent over 25 years working in the fields of IT, corporate operations, and software development - most recently at Apple. Mike volunteers his time serving as a Board member with the LLVM Foundation, focusing on finance and operations. Mike truly believes in the power of AI to help address some of the worldâs greatest needs.



Mostafa Hagog

Performance & Compiler Engineering Lead

Mostafa is a seasoned engineering leader in high-performance computing. During his tenure at NVIDIA, he served as Engineering Director and led teams to develop optimized deep learning libraries like cuDNN and CUTLASS, revolutionizing GPU-accelerated AI. At SiFive, as VP of Software, Mostafa assumed a leadership role guiding teams in the development of an MLIR/LLVM-based software stack for SiFive Intelligence & performance cores. His contributions also extend to optimizing Intel GPU hardware/software features, playing a pivotal role in developing the AVX1/2 SIMD ISA for Intel CPUs, and contributing to the GNU C Compiler. Mostafa holds a Master of Science in Electrical Engineering from the Technion, with a specialization in compiler optimizations. His unwavering passion for innovation continues to drive advancements in the field of high-performance computing.



Nick Kreeger

Framework Engineering Lead

Software Engineering lead with over 15 years of experience working at Google, Microsoft and a handful of startups. Nick has contributed to many technologies in Machine Learning such as TensorFlow.js, TensorFlow Lite/Micro, and ONNX/ONNXRuntime. Nick enjoys spending his free time with family and enjoying the Minnesotan outdoors.



================================================================================

URL: https://www.modular.com/company/culture

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# A great culture is the key to creating a great company.

## What we believe

### Build products usersÂ love â¤ï¸

Customers first

We build technology to lift the world by solving our customers' problems. We understand our users' problems and use cases and know there is sometimes a trade-off between "what is important" and "what is interesting" when building technology. We are clear that our customers always come first, and we always deliver on our promises.

Build it right

We build high-quality production software that displays technical mastery inside and out. We make our infrastructure the right way because we understand how quickly technical debt compounds. We know how faster engineering, product development, and business metrics move with a solid foundation. We build scalable, modular, and reliable systems that meet our customers' needs.

Drive results

When our customers and their people succeed, our company succeeds. No matter their role, every employee contributes to our collective success. Individually and together, we are all aligned on what we are working towards and how we can contribute. But we don't just work on auto-pilot. We expect everyone to regularly step back and ask if we're measuring the right input and output. We all win when we drive results that actually matter.

### Empower people ðª

Ownership

We act on behalf of the entire organization and not just for ourselves and our local team. We operate on the assumption that each person is highly motivated and will drive their work with a bias towards action. We drive ownership by tracking progress toward our goals with clear expectations, responsible owners, and solid execution. We expect everyone to act like an owner, and once a decision is made, everyone will fully commit and move forward together.

Transparency

We are transparent by having decisions, calendars, directions, and plans open to everyone in the company. Anyone can constructively ask tough questions and feel safe to have answers provided directly. We expect people to be clear about what they know and open about what they donât. We want everyone to feel that they can be vulnerable in front of each other and feel safe to take risks because this is how we grow and innovate.

Hire the best, never stop learning

We hire great people who seek to constantly grow themselves and others around them. Great people donât settle; they have a thirst for knowledge and new skills, a desire for self-improvement, and actively seek feedback whenever they can. Great people develop great people; they take coaching others seriously and look to raise the standards of the whole organization.

Have fun, live life

We believe people do their best work when they are happy. Our goal is to ensure that you will always have the environment to achieve the right mix of family, compensation, growth, and mission to live a balanced and fulfilling life. By having fun and living your life, you also see more of the world, appreciate it, and learn how we can help improve it.

### Be an incredible teamÂ ð¤

Win together, fail together

Changing the world is a team sport, and we need a group of incredibly talented people to be successful. Individual wins, or mistakes, matter less than everyone winning, failing, learning, and growing together. We expect everyone to listen, speak openly, and treat others with respect regardless of the wins or losses along the way. We foster a blameless culture for mistakes, and we expect our team to take risks without feeling insecure or embarrassed. The more we trust each other, the more we can lean on each other, and the more we can learn and grow together.

Own inclusion, be diverse

We build for everyone, and we are open to everyone. We believe that the best results come from a team that reflects the world at large. Our customers are diverse, and so are their needs, so we must empower and promote diversity and diverse perspectives at all company levels. We need to build diverse teams and foster diverse thinking to create products that genuinely help the world.

Everyone has a voice

We expect the best concept, design, plan, or direction to succeed regardless of someoneâs ârankâ in the company. Innovation exists everywhere, and great ideas are inside all of us. You can always reach out to anyone at any level in the company. We foster open communication and constructive debate and expect everyone to accept respectful challenges to their positions. We believe that the job of a leader is to find the right answer with their team, not to magically know everything themselves.

Assume positive intent

We expect everyone to assume the best of others. Our experiences have shown that information asymmetry almost always exists between people, and taking the time to understand circumstances via transparent communication is key to building healthy and functioning teams. We're all human and often jump to conclusions; we want people to assume good intentions when we do.

ð Weâre hiring!

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/company/careers

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# Join us in building the future of AI

Watch Video

## We want to enable AI to be used by anyone, anywhere.

Our ambitions are enormous, but working here will feel very familiar. You can change the world without giving up your life

## Why Modular?

Grow with the best

Build with some of the industry's best AI leaders.

Maximize how you work

We will always push the limits to create the best possible environment for our people and teams.

Read how we work.

Build AI for the world

Build a next-generation developer platform, with production quality infrastructure, for the world.

Have fun, live life

Regular team onsites, local meetups & fun, strong team collaboration and more.

## Work and life, balanced.

ð©º

Leading medical, dental & vision packages

ðµ

Strong compensation & equity packages

ð¼

Generous maternity & paternity leave

ð

401K Plan

ð

Work wherever you want

ðï¸

Unlimited Vacation & PTO

ð¥³

Corporate perks & epic team fun

## Current Job Openings

Come and be part of a world-class team that is rebuilding AI for everyone. We welcome applications from all backgrounds and communities.

Engineering

(0)

Text

Location

## What you expect from the interview

Modular has a straight-forward interview process and a large majority of the roles follow the same steps:

Step 01

### Application Review

The first step of the process is to submit your resume. We aim to review all resumes within 2 weeks and we respond to every single application made.

Step 02

### Call with Recruiting

Typically you will first have a short 20-30min call with recruiting to just go over some basic information about you and the company, and to discuss next steps.

Step 03

### Call with Hiring Manager

The Hiring Manager will dig a bit deeper into your background and previous experience to determine mutual fit. This will also be your opportunity to learn more about what the team is working on and what the role entails. In case you have applied for more than one role on different teams, we often schedule you for two separate calls with two separate Hiring Managers.

Step 04

### Team Interviews

There are typically 4 team interviews, usually 45-60 minutes each. These may be scheduled all at once or separately, depending on the role. The focus areas for these team interviews vary by role. For example, for Engineering roles, you can expect coding and system design type interviews; for Product roles, we often talk about the industry and product strategy. For Leadership roles, we may ask for a presentation.We believe that team culture is a critical part of a great company, so every candidate also has a Culture interview that focuses on communication skills, team work, and so on. Â We will let you know what to expect in each interview when we send you a schedule confirmation.

Step 05

### Founder Interviews

We aim to put all finalists in touch with at least one of our co-founders before a final decision. This would allow you as a candidate to speak directly to Chris and/or Tim, and get answers to any final important questions you may have.

Step 06

### Offer

If we make you an offer, we share an extensive offer deck for you and schedule a walk-through call to go over the information in detail and again answer any final questions you may have. We also aim to give you enough time to make a decision after that.

## Why should you be excited aboutÂ Modular?

âI picked modular because of the people and what they believe in and what they want to do. I want to be a part of something that makes a difference for the better.â

Paige Bedwell

Program Manager

âThe team is incredible. They're very smart. They're experts in their field, and we're solving the hardest computational problems in the world.â

Abdul Dakkak

AI Compiler Engineer

âThe best part of working here is a strong commitment to culture. I work on the most challenging problems, have the freedom to do it anywhere, and have a huge impact on AI.â

Eric Johnson

Product Lead

## Frequently asked questions

Are interviews conducted in-person?

Typically of our interviews are conducted over Zoom video. For coding interviews, we use Coderpad and you will receive a link from your interviewer. In some cases we may fly you in to meet some of our leaders for the last interview.

What do you look for in a candidate?

There is no âone size fits allâ answer here however Modulers are passionate about their work, intellectually curious, humble, and ready to roll up their sleeves. We like to have discussions in interviews and expect you to ask clarifying questions and talk us through your thought process â sometimes there is no right or wrong answer, we just want to understand how you think and work through challenging problems. We also look for strong alignment with Modularâs Culture and values.

I donât meet all of the requirements for a role, can I still be considered?

We would love to receive your application even if it doesnât meet all criteria! We review applications with all roles and opportunities in mind. Sometimes we may reach out to you to offer or suggest another role or team that might be a better fit for you, or we offer to keep your application on file and reach out a few months down the line once more suitable opportunities open up.

How long does the interview process take?

On average it takes candidates about 4 weeks to go through the full interview process however if you require a decision by a certain date, please speak to your recruiter.

Do you offer internships?

We want to ensure that our interns have an outstanding experience, which includes a well-defined project and goals, meaningful and impactful work, and strong and attentive mentorship. As we are still a small team, we are not ready to offer internships at this time but we will be sure to update the website as soon as it changes!

Do you provide visa assistance?

Yes, we offer premium processing for our H1-B transfer candidates. We can also sponsor H1-B or offer other relevant visa application assistance to eligible candidates.

Do you provide Green Card assistance?

Yes, we also offer Green Card application assistance to eligible candidates, just speak to your recruiter for more details.

Do you provide relocation assistance?

As we are a remote-first company, we generally do not relocate candidates except new graduates who are moving away from campus. If Modular asks you to relocate, we will also provide you with a relocation stipend.

Are there any opportunities for professional development?

Modular is growing rapidly so there are a variety of growth opportunities available â whether thatâs moving into a TL role, management, or a lateral transfer to a different team. We provide our managers with training to walk you through your career planning, and we have a well-defined but lightweight performance review process that ensures your career goals are discussed at a regular cadence.

I applied some time ago but havenât heard back?

We aim to respond to every single candidate, and if you have applied to more than one role, we process all of your applications separately. Sometimes these emails end up in spam, so please check your spam folder. If you havenât heard from us, you can contact your recruiter or careers@modular.com to follow up.

Will you provide feedback if I am rejected?

We sincerely appreciate the time it takes to interview, so we always provide some feedback as to why your application did not work out if we have spoken to you. If you have not heard from us within 2 weeks after your last call, please follow up with your recruiter.

What benefits do you offer?

Modular offers some world-class benefits for all employees. Our benefit package changes over time, and may depend on your specific location, but US-based employees can expect benefits that include:

- A variety of fantastic health benefits (health, dental, vision insurance; life insurance etc) are available.

- A 401k plan with up to 5% match.

- Access to Passport Corporate Discount Program, LinkedIn Learning, and free tax advice on Carta.

- Generous work-from-home stipend of $1500 to help you improve your home office.

- Unlimited paid time off and flexible work hours.

Who we are

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/community

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# Â We're Â buildingÂ theÂ future of AIÂ together

## Resources to getÂ youÂ started!

Build and learn together

Get the code and contribute

Read the docs

Monthly newsletter

Latest news and thoughts

Blogs and tutorials

Download MAX

## Over 300 open-source projects created by the community

### llama2.ð¥

Inference forLlama2 models in a single file of Mojo

### Kamoð¥

A personal journey into learning about KolmogorovâArnold Networks using Mojo.

### Fire Physics Engine

A 2D physics engine built in Mojo ð¥

### Prism

A budding CLI library

+300

more open source projects

## Livestreams and Webinars on YouTube

MAX Graph API Tutorial

1k ViewsÂ . Streamed July 16, 2024

Getting started with MAX release and nightly builds

1.5K viewsÂ . Streamed June 3, 2024

Speed up K-Means clustering by porting Python implementation to Mojoð¥

3k ViewsÂ . Streamed May 30, 2024

Contributing to Open-Source Mojoð¥ Standard Library

1.1k viewsÂ . Streamed May 14, 2024

Mojoð¥: a deep dive on ownership with Chris Lattner

33k viewsÂ . Streamed May 13, 2024

MAX Graph API Tutorial

1k ViewsÂ . Streamed July 16, 2024

Getting started with MAX release and nightly builds

1.5K viewsÂ . Streamed June 3, 2024

## Community openÂ sessions

Mojoð¥ Community Meeting #6

1.6kÂ . Streamed August 14, 2024

Mojo ð¥ Community Meeting #5

2.5k viewsÂ . Streamed July 29, 2024

Mojo ð¥ Community Meeting #4

1:3k viewsÂ . Streamed July 22, 2024

Mojo Community Meeting #3

2.5k viewsÂ . Streamed June 18, 2024

Mojo Community Meeting #2

2.2k viewsÂ . Streamed June 7, 2024

Mojo Community Meeting #1

2.4k viewsÂ . Streamed May 23, 2024

Mojoð¥ Community Meeting #6

1.6kÂ . Streamed August 14, 2024

Mojo ð¥ Community Meeting #5

2.5k viewsÂ . Streamed July 29, 2024

## Join the Modverse

22K+ members

18k members

11k members

22k stars

## Get started now

- Getting started guideStep by step walkthrough on how to get started

- Browse examplesRun a project locally in just 2min

- Connect with buildersJoin over 22,000 developers

Getting started guide

Step by step walkthrough on how to get started

Browse examples

Run a project locally in just 2min

Connect with builders

Join over 22,000 developers

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/company/contact

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# Connect with our sales team

Tell us what tools your organization is using and we can work together to see how best to incorporate MAX.

Thank you for your submission.

Your report has been received and is being reviewed by the Sales team. A member from our team will reach out to you shortly.

Thank you,

Modular Sales Team

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog/bring-your-own-pytorch-model

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

July 9, 2024

# Bring your own PyTorch model

Modular Team

â

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

PyTorch has become the top choice for researchers pushing the boundaries of AI, thanks to its blend of flexibility, Pythonic simplicity, and robust community support. Despite its popularity and success in research environments, PyTorch faces challenges in large-scale production deployments. What makes PyTorch excellent for developmentâlike its Python eager modeâcauses difficulties in production settings where resource management, latency targets, and reliability are critical.

While research teams favor Python for its ease of use, deployment teams often use high-performance languages and libraries like C++ and CUDA to optimize models for latency, throughput, and cost efficiency. Despite efforts to optimize the PyTorch deployment processes (e.g., TorchScript), a universal method for deploying high-performance PyTorch models at scale remains elusive.

Recently, developers have resorted to building point-solutions to deploy LLMs (that is, use-case-specific solutions such as TRT-LLM and vLLM), which further fragment the industry and increase complexity for everyone.

â

This ongoing challenge underscores the complexities inherent in bridging the gap between research-driven AI development and robust, scalable AI deployment. But it also describes one of the core reasons we built MAXâthe Modular Accelerated Xecution platform.

Simply put, MAX provides the best way to deploy PyTorch into production.

### MAX unlocks the full power of PyTorch

MAX provides inference API backed by a state-of-the-art compiler and inference runtime that works with a variety of models and hardware types, across local laptops and common cloud instances. Importantly, MAX doesn't require that you rewrite your PyTorch modelsâit meets you where you are now, using your existing model and existing code, with minimal changes to adopt our inference API (available in Python and C). Over time, you can incrementally add more MAX features for more performance, programmability, and portability.

MAX provides the following benefits for PyTorch deployments:

- Optimized performance: MAX provides advanced compiler and runtime optimizations that improve your resource efficiency and resource management. With only a few lines of code, MAX accelerates your models, reducing latency, improving user experience, and saving you valuable compute costs and resources. Relative to stock PyTorch, MAX runs PyTorch models up to 5x faster on CPU, depending on the specific workload and hardware. And GPUs are coming soon!

- Full compatibility: MAX reduces fragmentation in your workflow, by meeting you where you are now, with your existing PyTorch models, tools, and libraries. Weâve taken away the complexity in converting your Python models to high-performance languages. Instead of using brittle model translators, MAX is compatible with the PyTorch and ONNX ecosystems, which means your models work out of the box.

- Simple extensibility: MAX allows you to progressively upgrade your AI infrastructure over time, as you want to optimize the performance of your models. For even the most sophisticated AI engineers, performance-tuning AI pipelines is complicated because it involves advanced knowledge of system programming languages, AI hardware, and PyTorch itself. With MAX, youâre able to extend your models with custom operations (ops) written in Mojoâa new programming language that looks like Python and provides the performance of C. Importantly, custom ops written in Mojo are natively portable to any hardware that MAX supports and automatically fuse into the graph, ensuring peak performance.

MAX brings your PyTorch models to their full potential and scales much further to meet the demands of GenAI as it continues to rapidly evolve. MAX seamlessly integrates with your existing PyTorch models, provides an unparalleled boost in performance and efficiency, and is easily customizable, so you can focus on what you do best â innovating and creating.

### How to use MAX with PyTorch

As we mentioned earlier, there is no universal way to deploy PyTorch. As such, MAX provides a few different integration points with the PyTorch ecosystem. We detail each below.

#### MAX Engine for TorchScript Models

If you are using TorchScript, MAX Engine simplifies the process into three easy steps: load, compile, and execute.

#### MAX Engine for ONNX Models

Similarly, for ONNX models, the process is streamlined into load, compile, and execute steps.

#### Torch.compile (coming soon!)

And coming soon, we will also provide a MAX backend for PyTorch 2.xâs torch.compile API. Below is how we would optimize a Stable Diffusion model with MAX in this scenario.

Whether you're using cutting-edge PyTorch features such as torch.compile, or staying with more traditional ways of serving your models, such as TorchScript and ONNX, MAX is the best way to deploy your PyTorch workloads.

### MAX is free! Download now

By adopting MAX in your enterprise, you can take control over your PyTorch models. Join the growing community of developers who trust MAX for their model optimization needs. Don't let performance limitations hold you back. With MAX, you can elevate your PyTorch and ONNX models, delivering faster and more efficient results.

Get started with MAX today and experience the difference!

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Product

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Modular Team

Company

Our mission is to have real, positive impact in the world by reinventing the way AI technology is developed and deployed into production with a next-generation developer platform.



================================================================================

URL: https://www.modular.com/blog/develop-locally-deploy-globally

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

July 9, 2024

# Develop locally, deploy globally

Modular Team

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

In particular, it remains nearly impossible to build streamlined and scalable development workflows that bridge the gap between local development and cloud deployment. These workflows are complicated because AI tooling is fragmented, with each tool presenting its own trade-offs and limitations.

Todayâs AI developers must leverage a multitude of different tools across their end-to-end AI workflows. Some tools work for cloud accelerators but are limited to local CPUs, while others excel for local CPU development but arenât cloud-enabled. Working across these tools requires problematic abstractions and translators to unify fundamentally incompatible technologies, requiring rewriting of model or application code to make it all work together.

Sometimes, it seems impossible to piece together the right combination of tools and hardware to support the full developer lifecycle and provide the best performance-to-cost trade-off for any given model.

## How MAX helps

MAX solves these problems by providing a unified inference API backed by a state-of-the-art compiler and inference runtime that works with a variety of models and hardware types across local laptops and common cloud instances. MAX doesn't require you to migrate your entire AI pipeline and serving infrastructure to something newâit meets you where you are now and allows you to upgrade incrementally.

MAX makes your pipeline portable across a wide range of CPU architecturesâIntel, AMD, and ARMâand GPUs, opening up more portability and performance. MAX allows you to take advantage of the breadth and depth of different cloud instances at the best price, ensuring you always get the best inference cost-performance ratio.

### MAX unlocks local to cloud

With MAX, you can develop AI applications locally and then easily package them for deployment to any cloud environment. This unified workflow offers several critical advantages:

- Speed and Flexibility: Downloading MAX enables local development for rapid iteration and testing. Importantly, MAX doesnât just run locally but is faster on many use cases than industry standard local LLM frameworks, like llama.cpp, making development even faster. You can quickly modify code, run experiments, and debug issues without the setup and latency associated with remote servers.

- Resource Availability: On local machines, you have direct access to local hardware resources, such as CPU and GPUs, for free. Spinning up and paying for cloud instances is unnecessary and isnât bottlenecked by availability.

- Control and Customization: Local environments can be tailored to specific project needs, allowing developers to install custom libraries, frameworks, and tools without restrictions. Importantly, for latency-sensitive applications, fully controlled local development boxes provide less noise than shared cloud instances.

- Consistency: Developing with MAX locally and bundling our infrastructure into OCI-compatible containers like Docker means you can ensure that what you build locally works seamlessly in production environments.

- Global Scale: Finally, MAX is highly optimized for the AI hardware running in popular data center cloud providers like AWS, Azure, and GCPâincluding CPU and GPUs (coming soon!). Users can scale their AI applications globally with no code changes and state-of-the-art performance.

## How MAX works

The industry lacks a unified AI infrastructure platform and is instead flush with âpoint solutionsâ that only work for specific models, hardware, and OSâs. When we began the effort to unify the world's AI infrastructure, we realized fragmentation in the stack was driven by a lack of a common programming model for AI hardware. That's why we created Mojo, a new unifying programming language for AI hardware that combines Pythonâs expressiveness with Câs performance.

Mojo is the core technology that provides the foundation for the rest of the MAX platform. The MAX Engine, our next-generation graph compiler and runtime system, leverages Mojo to implement its low-level mathematical operations, such as MatMul. This provides unparalleled portability and performance and makes local-to-cloud workflows possible. You can load any model into MAX Engine and achieve low-latency inference on a wide range of hardware.

All that said, you do not need to use Mojo to use MAX. You can bring your existing models to the framework and execute them with MAX Engine using our API libraries in Python and C. However, using Mojo with MAX gives you superpowers. Mojo allows you to write custom ops for your model or write a full inference graph for optimal performance in MAX Engine.

Finally, integrating MAX as part of your AI development workflow makes it easy to deploy your locally developed models into production using trustworthy tools that include robust scaling, monitoring, and deployment templates.

With MAX, you can serve your model using industry-compatible tools, including NVIDIA's Triton Server. Because we already interoperate with the tools you already use, we make it easy to drop MAX into your existing workflows that require model versioning, multi-model deployment, and support for various deployment environments, making it a versatile solution for AI inference deployment. Read about how easy this is right now!

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Product

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Modular Team

Company

Our mission is to have real, positive impact in the world by reinventing the way AI technology is developed and deployed into production with a next-generation developer platform.



================================================================================

URL: https://www.modular.com/enterprise

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# Accelerate AI innovation and scaleÂ globally.

Run AIÂ workloads more efficiently, and optimize your compute inside your enterprise.

Power all your AI use cases on one stack.

### Fastest GPU Infrastructure

Get out of the box performance for GenAI models on NVIDIA H100s and A100s.

### Unify your AI infrastructure stack

Unify industry frameworks and hardware, streamlining your deployment workflows to any cloud or on-prem environment.

### Deploy and scale for FREE with MAX

Package your pipelines once and deploy across CPUs and GPUs without having to change any code.

### Easiest way to optimize your existing models

Drop in your PyTorch or ONNX models and get an instant boost in performance with our next generation inference runtime.

- Deploy MAX inside your cloud environment

- Supercharge the efficiency of your AI stack with just 3 lines of code.

- Dedicated support from our world class AI infrastructure team.

Deploy MAX inside your cloud environment

Supercharge the efficiency of your AI stack with just 3 lines of code.

Dedicated support from our world class AI infrastructure team.

## Talk to our Sales Team

Tell us what tools your organization is using and we can work together to see how best to incorporate MAX.

## Get started now

- Getting started guideStep by step walkthrough on how to get started

- Browse examplesRun a project locally in just 2min

- Connect with buildersJoin over 22,000 developers

Getting started guide

Step by step walkthrough on how to get started

Browse examples

Run a project locally in just 2min

Connect with builders

Join over 22,000 developers

Download for your platformÂ now

- 

- 

- 

View Pricing

Contact Sales

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

MAX on GPU waitingÂ list

Be the first to get lightning fast inference speed on your GPUs. Be the envy of all your competitors and lower your compute spend.

================================================================================

URL: https://www.modular.com/ai-resources

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

## AI Resources

AI resources provides a centralized hub for the latest and most relevant research papers on large language models (LLMs), Generative AI, machine learning (ML) systems, optimizations and compilers. These resources offer researchers, developers, and hackers easy access to the latest research. By distilling complex information into digestible summaries, AI resources help streamline the research process, enabling quicker implementation of new ideas and techniques into practical applications across the AI stack.

Browse by category

## LLM Context Evaluations

Read more

## High Performance Computing (HPC) Technical Primer

Read more

## Ring Attention with Blockwise Transformers for Near-Infinite Context

Read more

## Attention with Linear Biases Enables Input Length Extrapolation (ALiBi)

Read more

## YaRN: Efficient Context Window Extension of Large Language Models

Read more

## Gemini: A Family of Highly Capable Multimodal Models

Read more

## Gemini 1.5: Unlocking multimodal understanding across millions of tokens of context

Read more

## Efficient Memory Management for LLM Serving with PagedAttention

Read more

## MLIR: A Compiler Infrastructure for the End of Mooreâs Law

Read more

## RoBERTa: A Robustly Optimized BERT Pretraining Approach

Read more

## Large Language Model Technical Primer

Read more

## Contrastive Language-Image Pre-training (CLIP)

Read more

## ML Compiler Technical Primer

Read more

## AI & Memory Wall

Read more

## Quantization Technical Primer

Read more

## Mixtral of Experts

Read more

## Llama 2

Read more

## Byte Pair Encoding (BPE)

Read more

## FlashAttention

Read more

## FlashAttention-2

Read more

## Mistral-7B

Read more

## Phi-3-mini

Read more

## Gemma: Open Models Based on Gemini Research and Technology

Read more

## Grouped Query Attention

Read more

## Rotary Position Embedding (RoPE)

Read more

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/company/report-issue

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# Report an Issue

At Modular, we care deeply about safety, privacy, and security.

If you have discovered a potential issue related to safety, privacy, or security, please fill out this form with the following details:

- A summary of the observed and expected behaviors

- Steps to reproduce

- Details of the environment you are using

A summary of the observed and expected behaviors

Steps to reproduce

Details of the environment you are using

We truly appreciate your initiative to help improve our security. And note that at this time we cannot provide a personal response to each report.

If you instead found an ordinary bug (not a safety/privacy/security issue), please instead report it here on GitHub.

Thank you for your submission.

Your report has been received and is being reviewed by the Sales team. A member from our team will reach out to you shortly.

Thank you,

Modular Sales Team

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/legal/terms

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# Terms of Use

Last Modified: July 18th, 2024

These Terms of Use (this âAgreementâ), effective as of the date on which you check a box, click a button, download any Client-Side Software, or otherwise acknowledge your acceptance of this Agreement, is by and between Modular Inc. with offices located at 228 Hamilton Ave, Palo Alto, CA 94301, USA (âModularâ, âweâ, or âusâ) and you. This Agreement constitutes a binding contract between you and Modular, and your use of the Max Platform (as defined below) is at all times subject to the terms and conditions outlined in this Agreement.

This Agreement incorporates by reference all other terms and policies governing your use of the Max Platform, including our MAX & Mojo Community License (https://www.modular.com/legal/max), Privacy Policy (https://www.modular.com/legal/privacy), Acceptable AI Use Policy (https://www.modular.com/legal/aup), Mojo Champions Policy (https://www.modular.com/legal/champions) as well as any other policies we may adopt from time to time, including terms for any contests or events we host or sponsor on the Max Platform. Capitalized terms used herein but not otherwise defined have the meanings set forth in the applicable supplemental terms.

##### 1. Definitions

1.1 - "Account" means the account you set up to access the Max Platform, and represents your legal and services relationship with Modular.

1.2 - "Affiliate" means an entity that controls, is controlled by, or is under common control with you.

1.3Â - âCodeâ means any software code that you write, develop, or otherwise import for use on the Max Platform.

1.4 - âClient-Side Softwareâ means any Modular software in source or object code form that Company makes available to Customer for use in connection with the Max Platform, including but not limited to any Modular SDKs.

1.5Â - âDerivative Dataâ means data and information related to or derived from Users of the Max Platform that has been aggregated and/or anonymized by Modular, including any metrics, feedback, and ratings relating to any Code.

1.6 - âDeveloperâ means a User who wrote or otherwise contributed to Code.

1.7 - âHarmful Codeâ means any software, hardware, or other technology, device, or means, including any virus, worm, malware, or other malicious computer code, the purpose or effect of which is to permit unauthorized access to, or to destroy, disrupt, disable, distort, or otherwise harm or impede in any manner any (i) computer, software, firmware, hardware, system, or network; or (ii) any application or function of any of the foregoing or the security, integrity, confidentiality, or use of any data processed thereby.

1.8 - âMax Platformâ means Modularâs proprietary, hosted software platform you use to access your Account, all Client-Side Software, and other website functionality as made available to Users from time to time at www.modular.com (or a successor or sub-site).

1.9Â - âModular IPâ means the Max Platform and any and all intellectual property provided to you or any other User in connection with the foregoing. For the avoidance of doubt, Modular IP includes Derivative Data, Usage Data, Documentation, and any information, data, or other content derived from Modularâs provision of the Max Platform but does not include User Generated Content.

1.10 - âThird-Party Productsâ means any third-party products provided with, integrated with, or incorporated into the Max Platform.

1.11 - âUser,â âyou,â and âyourâ refer to the individual person, company, or organization that has visited or is using the Max Platform; that accesses or uses any part of an Account; or that directs the use of the Account in the performance of its functions. A User must be at least 18 years of age.

1.12 - âUser Generated Contentâ means information, data, and other content, in any form or medium, that is submitted, posted, or otherwise transmitted by a User through the Max Platform, including any Code; provided that, for purposes of clarity, User Generated Content as defined herein does not include Derivative Data.

##### 2. Account Registration and Requirements

2.1 - Registration. You must provide a valid email address and password in order to complete the Account signup process. Account registration and provisioning is at Modularâs sole discretion, and signing up for our waitlist does not guarantee that you will be eligible for account registration.

2.2 - Requirements.Â

A) You must be a human to create an Account. Accounts registered by "bots" or other automated methods are not permitted.

B) You must be age 18 or older. The Childrenâs Online Privacy Protection Act (âCOPPAâ) requires that online service providers obtain parental consent before they knowingly collect personally identifiable information online from children who are under thirteen (13). We do not knowingly collect or solicit personally identifiable information from children under thirteen (18). Users under the age of 18 are not permitted to use Modular.

C) Modular does not target its Max Platform to children under 18, and we do not permit any Users under 18 on our Max Platform. If we learn of any User under the age of 18, we will terminate that Userâs Account immediately.

D) Your login may only be used by one person. You may not share your Account with others, and you may not use anyone elseâs Account.

2.3 - Account Security. You are responsible for keeping your Account secure while you use the Max Platform. We offer tools to help you maintain your Account's security, but the content of your Account and its security are your responsibility. You are responsible for all content posted and activity that occurs under your Account (even when content is posted by others who have Accounts under your Account). You are responsible for maintaining the security of your Account and password. Modular cannot and will not be liable for any loss or damage from your failure to comply with this security obligation. You will promptly notify Modular if you become aware of any unauthorized use of, or access to, our Max Platform through your Account, including any unauthorized use of your password or Account.

##### 3. MAXÂ Platform Access and Use.

Subject to and conditioned on your compliance with the terms and conditions of this Agreement, Modular hereby grants you a right to access and use the Max Platform on a non-exclusive, non-transferable, and non-sublicensable basis. The foregoing includes a limited license for Customer to install and use the Client-Side Software in accordance with the MAXÂ SDK("MAX") Community License Terms (https://www.modular.com/legal/max).

3.1 - Use Restrictions. You shall not use the Max Platform for any purposes beyond the scope of the access granted in this Agreement. You shall at all times comply with ModularâsAcceptable AI Use Policy (https://www.modular.com/legal/aup).

3.2 - Mojo-Specific Restrictions. If you use Mojo to write or develop any Code, you are strictly prohibited from deploying such Mojo-based Code to any Graphics Processing Units (GPUs), Tensor Processing Units (TPUs),or other specialized hardware. You agree that deployment of such Code shall be limited exclusively to Central Processing Unit (CPU) instances. Non-compliance may result in account suspension or termination of support without prior notice. Requests for exceptions to this restriction must be formally submitted to Modular in writing for approval (which Modular may withhold at its sole discretion).

3.3 - Reservation of Rights. Modular reserves all rights not expressly granted to you in this Agreement. Except for the limited rights and licenses expressly granted under this Agreement, nothing in this Agreement grants, by implication, waiver, estoppel, or otherwise, to you or any third party any intellectual property rights or other right, title, or interest in or to the Modular IP.

3.4 - Suspension. Notwithstanding anything to the contrary in this Agreement, Modular may temporarily suspend your Account and/or access to the Max Platform if: (i) Modular reasonably determines that(a) there is a threat or attack on any of the Modular IP; (b) your or another Userâs use of the Modular IP disrupts or poses a security risk to the Modular IP or to any other User, customer, or vendor of Modular; (c) you are using theModular IP for fraudulent or illegal activities or in violation of theAcceptable AI Use Policy; (e) Modularâs provision of the Max Platform to you is prohibited by applicable law; or (f) any User Generated Content (including any Code) submitted, posted, or otherwise transmitted by you through the MaxPlatform may infringe or otherwise violate any third partyâs intellectual property or other rights; (ii) any vendor of Modular has suspended or terminated Modularâs access to or use of any Third-Party Products required to enable up to access the Max Platform; or (iii) in accordance with a violation of any other term of this Agreement (each of (i), (ii), or (iii), a âServiceSuspensionâ). Modular shall use commercially reasonable efforts to provide written notice of any Service Suspension to you and to provide updates regarding resumption of access to the Max Platform following any Service Suspension. Modular shall use commercially reasonable efforts to resume providing access to the Max Platform as soon as reasonably possible after the event giving rise to the Service Suspension is cured. Modular will have no liability for any damage, liabilities, losses (including any loss of data or profits), or any other consequences that you or any other User may incur as a result of a Service Suspension.

3.4 - Derivative Data. Notwithstanding anything to the contrary in this Agreement, Modular may monitor your use of the MAXÂ Platform and collect and compile Derived Data. As between you and Modular, all right, title and interest in Derivative Data , and all intellectual property rights therein, belong to, and are retained solely by, Modular. You acknowledge that Modular may compile Derivative Data based on User Generated Content input into, and transmitted via, the MAXÂ Platform. Not withstanding anything to the contrary in this Agreement, you further acknowledge that Modular may use and disclose Derivative Data for any lawful purpose.

##### 4. User Responsibilities.

4.1 - General. You are at all times responsible and liable for all uses of the Max Platform resulting from access from your Account, directly or indirectly, whether such access or use is permitted by or in violation of this Agreement.

4.2 - User Generated Content. You shall not upload to the Max Platform any User Generated Content that you do not have sufficient rights to upload. You hereby represent and warrant that you have sufficient rights to use any Code you upload to or otherwise use or incorporate as part of the Max Platform. You shall at all times abide by and comply with the Modular Acceptable AI Use Policy (https://www.modular.com/legal/aup). You hereby grant to Modular a non-exclusive, royalty-free, worldwide license to reproduce, distribute, and otherwise use and display your User Generated Content and perform all acts with respect to your User Generated Content as may be necessary for Modular to provide the Max Platform, and a non-exclusive, perpetual, irrevocable, royalty-free, worldwide license to reproduce, distribute, modify, and otherwise use and display your User Generated Content incorporated within the Derivative Data, and as otherwise necessary to provide you with Max Platform functionality. The foregoing includes, without limitation, the right for Modular to display email domains on the Max Platform associated with your Account to help you find other Users from your organization or with whom you may share other professional associations.

4.3 - Third-Party Products. Modular may from time to time make Third-Party Products available to you or may allow for certain Third-Party Products to be integrated with the Max Platform to allow for the transmission of Code or other User Generated Content from such Third-Party Products into the Max Platform (including, for example and without limitation, Github, AWS Code Commit, BitBucket, etc.). For purposes of this Agreement, such Third-Party Products are subject to their own terms and conditions. Modular is not responsible for the operation of any Third-Party Products and makes no representations or warranties of any kind with respect to Third-Party Products or their respective providers. If you do not agree to abide by the applicable terms for any such Third-Party Products, then you should not install or use such Third-Party Products. By authorizing Modular to transmit your User Generated Content from Third-Party Products into the Max Platform, you represent and warrant to Modular that you have all right, power, and authority to provide such authorization.

4.4 - User Controls and Responsibility. You have and will retain sole responsibility for: (i) all your own User Generated Content; (ii) your technology infrastructure and network and internet connection(s) from which you access the Max Platform; (iii) the security and use of your Account and associated credentials; and (iv) all access to and use of the Max Platform, including all results obtained from, and all conclusions, decisions, and actions based on, such access or use.

##### 5. Account Fees.

You may access the Max Platform for free, or we may charge a fee for using the Max Platform (the âPaid Servicesâ).

5.1 - Paid Platform Access. Certain aspects of features of the Max Platform may be subject to payments now or in the future (âAccount Feesâ).

A) Payment Processor. We use a third-party payment processor (the âPayment Processorâ) to bill you through a payment account linked to your Account on the Max Platform (your âBilling Accountâ) for any owed Account Fees. The processing of payments will be subject to the terms, conditions and privacy policies of the Payment Processor in addition to this Agreement. Currently, we use Stripe, Inc. as our Payment Processor. You can access Stripeâs Terms of Service at https://stripe.com/us/checkout/legal and their Privacy Policy at https://stripe.com/us/privacy. We are not responsible for any error by, or other acts or omissions of, the Payment Processor. By choosing to use our Paid Services, you agree to pay us, through the Payment Processor, all Account Fees then in effect for any use of such Paid Services in accordance with the applicable payment terms, and you authorize us, through the Payment Processor, to charge your chosen payment provider (your âPayment Methodâ). You agree to make payment using that selected Payment Method. We reserve the right to correct any errors or mistakes that the Payment Processor makes even if it has already requested or received payment.

B) Payment Method. The terms of your payment will be based on your Payment Method and may be determined by agreements between you and the financial institution, credit card issuer or other provider of your chosen Payment Method. If we, through the Payment Processor, do not receive payment from you, you agree to pay all amounts due on your Billing Account upon demand.

C) Recurring Billing. Some of the Paid Services may consist of an initial period, for which there is a one-time charge, followed by recurring period charges as agreed to by you. By choosing a recurring payment plan, you acknowledge that such Paid Services have an initial and recurring payment feature and you accept responsibility for all recurring charges prior to cancellation. WE MAY SUBMIT PERIODIC CHARGES (E.G., MONTHLY) WITHOUT FURTHER AUTHORIZATION FROM YOU, UNTIL YOU PROVIDE PRIOR NOTICE (RECEIPT OF WHICH IS CONFIRMED BY US) THAT YOU HAVE TERMINATED THIS AUTHORIZATION OR WISH TO CHANGE YOUR PAYMENT METHOD. SUCH NOTICE WILL NOT AFFECT CHARGES SUBMITTED BEFORE WE REASONABLY COULD ACT. TO TERMINATE YOUR AUTHORIZATION OR CHANGE YOUR PAYMENT METHOD, GO TO ACCOUNT SETTINGS (https://developer.modular.com).

D) Current Information Required. YOU MUST PROVIDE CURRENT, COMPLETE AND ACCURATE INFORMATION FOR YOUR BILLING ACCOUNT. YOU MUST PROMPTLY UPDATE ALL INFORMATION TO KEEP YOUR BILLING ACCOUNT CURRENT, COMPLETE AND ACCURATE (SUCH AS A CHANGE IN BILLING ADDRESS, CREDIT CARD NUMBER, OR CREDIT CARD EXPIRATION DATE), AND YOU MUST PROMPTLY NOTIFY US OR OUR PAYMENT PROCESSOR IF YOUR PAYMENT METHOD IS CANCELED (E.G., FOR LOSS OR THEFT) OR IF YOU BECOME AWARE OF A POTENTIAL BREACH OF SECURITY, SUCH AS THE UNAUTHORIZED DISCLOSURE OR USE OF YOUR USER NAME OR PASSWORD. CHANGES TO SUCH INFORMATION CAN BE MADE AT ACCOUNT SETTINGS (https://developer.modular.com). IF YOU FAIL TO PROVIDE ANY OF THE FOREGOING INFORMATION, YOU AGREE THAT WE MAY CONTINUE CHARGING YOU FOR ANY USE OF PAID SERVICES UNDER YOUR BILLING ACCOUNT UNLESS YOU HAVE TERMINATED YOUR PAID SERVICES AS SET FORTH ABOVE.

E) Change in Amount Authorized. If the amount to be charged to your Billing Account varies from the amount you preauthorized (other than due to the imposition or change in the amount of state sales taxes), you have the right to receive, and we shall provide, notice of the amount to be charged and the date of the charge before the scheduled date of the transaction. Any agreement you have with your payment provider will govern your use of your Payment Method. You agree that we may accumulate charges incurred and submit them as one or more aggregate charges during or at the end of each billing cycle.

F) Reaffirmation of Authorization. Your non-termination or continued use of a Paid Service reaffirms that we are authorized to charge your Payment Method for that Paid Service. We may submit those charges for payment and you will be responsible for such charges. This does not waive our right to seek payment directly from you. Your charges may be payable in advance, in arrears, per usage, or as otherwise described when you initially selected to use the Paid Service.

G) Free Trials and Other Promotions. Any free trial or other promotion that provides access to a Paid Service must be used within the specified time of the trial. You must stop using a Paid Service before the end of the trial period in order to avoid being charged for that Paid Service. If you cancel prior to the end of the trial period and are inadvertently charged for a Paid Service, please contact us at hello@modular.com.

##### 6. Confidentiality.

6.1 - Your Confidentiality Obligations. You agree that any non-public information we give you, such as information about a private beta offering or any information or materials made available on non-public portions of the Max Platform, is Modularâs confidential information, regardless of whether it is marked or identified as such (collectively, âConfidential Informationâ). You agree to only use such Confidential Information for the express purpose of testing and evaluating such beta products and not for any other purpose. You should use the same degree of care as you would with your own confidential information, but no less than reasonable precautions to prevent any unauthorized use, disclosure, publication, or dissemination of our Confidential Information. You promise not to disclose, publish, or disseminate any Confidential Information to any third party, unless we donât otherwise prohibit or restrict such disclosure (for example, you might be part of a Modular-organized group discussion about a private beta feature).

6.2 - Exceptions. Confidential Information will not include information that is: (a) or becomes publicly available without breach of this Agreement through no act or inaction on your part (such as when a private beta feature becomes part of our publicly offered Max Platform); (b) known to you before we disclose it to you; (c) independently developed by you without breach of any confidentiality obligation to us or any third party; or (d) disclosed with permission from Modular. You will not violate the terms of this Agreement if you are required to disclose Confidential Information pursuant to operation of law, provided Modular has been given reasonable advance written notice to object, unless prohibited by law.

##### 7. Data Security and Processing of Personal Information.Â

7.1 - Security Measures. Modular will implement and maintain commercially reasonable administrative, physical, and technical safeguards designed to protect applicable User Generated Content from unauthorized access, use, alteration or disclosure.

7.2 - Processing of Personal Information; No Sensitive Data. Modularâs rights and obligations with respect to Personal Information that it collects directly from you are set forth in Modularâs Privacy Policy (https://www.modular.com/legal/privacy).

##### 8. Intellectual Property Ownership; Feedback.

8.1 -Modular IP. You acknowledge that, as between you and Modular, Modular owns all right, title, and interest, including all intellectual property rights, in and to the Modular IP and, with respect to Third-Party Products, the applicable third-party providers own all right, title, and interest, including all intellectual property rights, in and to the Third-Party Products.

8.2 - Your User Generated Content. Modular acknowledges that, as between you and Modular, you and your licensors (if any) retain all right, title, and interest, including all intellectual property rights, in and to User Generated Content.

8.3 - Feedback. If you sends us any communications or materials by mail, email, telephone, or otherwise, suggesting or recommending changes to the Modular IP, including without limitation, new features or functionality relating thereto, or any comments, questions, suggestions, or the like (âFeedbackâ), Modular is free to use such Feedback.

##### 9. Warranty Disclaimer.

MODULAR AND ITS LICENSORS, SUPPLIERS, PARTNERS, PARENT, SUBSIDIARIES OR AFFILIATED ENTITIES, AND EACH OF THEIR RESPECTIVE OFFICERS, DIRECTORS, MEMBERS, EMPLOYEES, CONSULTANTS, CONTRACT EMPLOYEES, REPRESENTATIVES AND AGENTS, AND EACH OF THEIR RESPECTIVE SUCCESSORS AND ASSIGNS (MODULAR AND ALL SUCH PARTIES TOGETHER, THE âMODULAR PARTIESâ) MAKE NO REPRESENTATIONS OR WARRANTIES CONCERNING THE MODULAR IP, AND THE MODULAR PARTIES WILL NOT BE RESPONSIBLE OR LIABLE FOR THE ACCURACY, AVAILABILITY, OCCURRENCE OF ERRORS, COPYRIGHT COMPLIANCE, LEGALITY, OR DECENCY OF MATERIAL CONTAINED IN OR ACCESSED THROUGH THE PLATFORM OR ANY CLAIMS, ACTIONS, SUITS PROCEDURES, COSTS, EXPENSES, DAMAGES OR LIABILITIES ARISING OUT OF USE OF, OR IN ANY WAY RELATED TO YOUR ACCESS OF THE PLATFORM OR USE OF ANY MODULAR IP. THE MODULAR PARTIES MAKE NO REPRESENTATIONS OR WARRANTIES REGARDING SUGGESTIONS OR RECOMMENDATIONS OFFERED THROUGH OR IN CONNECTION WITH YOUR USE OF THE PLATFORM. THE MODULAR IP IS PROVIDED BY MODULAR (AND ITS LICENSORS AND SUPPLIERS) ON AN âAS-ISâ BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, OR THAT USE OF THE MODULAR IP WILL BE UNINTERRUPTED OR ERROR-FREE. SOME STATES DO NOT ALLOW LIMITATIONS ON HOW LONG AN IMPLIED WARRANTY LASTS, SO THE ABOVE LIMITATIONS MAY NOT APPLY TO YOU.

##### 10. Indemnification.

You agree to indemnify and hold the Modular Parties harmless from and against any and all claims, liabilities, damages (actual and consequential), losses and expenses (including attorneysâ fees) arising from or in any way related to any claims relating to (a) your use of the Modular IP (including any actions taken by a third party using your Account), and (b) your violation or breach of any of the terms of this Agreement. In the event of such a claim, suit, or action (âClaimâ), we will attempt to provide notice of the Claim to the contact information we have for your Account (provided that failure to deliver such notice shall not eliminate or reduce your indemnification obligations hereunder).

##### 11. Limitation of Liability

TO THE FULLEST EXTENT ALLOWED BY APPLICABLE LAW, UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, TORT, CONTRACT, STRICT LIABILITY, OR OTHERWISE) SHALL ANY OF THE MODULAR PARTIES BE LIABLE TO YOU OR TO ANY OTHER PERSON FOR (A) ANY INDIRECT, SPECIAL, INCIDENTAL, PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND, INCLUDING DAMAGES FOR LOST PROFITS, BUSINESS INTERRUPTION, LOSS OF DATA, LOSS OF GOODWILL, WORK STOPPAGE, ACCURACY OF RESULTS, OR COMPUTER FAILURE OR MALFUNCTION, (B) ANY SUBSTITUTE GOODS, SERVICES OR TECHNOLOGY, (C) ANY AMOUNT, IN THE AGGREGATE, IN EXCESS OF THE GREATER OF (I) ONE-HUNDRED ($100) DOLLARS OR (II) THE AMOUNTS PAID AND/OR PAYABLE BY YOU TO MODULAR IN CONNECTION WITH THE ACCOUNT FEES FOR THE PLATFORM IN THE TWELVE (12) MONTH PERIOD PRECEDING THIS APPLICABLE CLAIM OR (D) ANY MATTER BEYOND OUR REASONABLE CONTROL. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL OR CERTAIN OTHER DAMAGES, SO THE ABOVE LIMITATION AND EXCLUSIONS MAY NOT APPLY TO YOU.

##### 12. General.Â

12.1Â - Assignment. You may not assign, delegate or transfer this Agreement or your rights or obligations hereunder, or your Account, in any way (by operation of law or otherwise) without Modularâs prior written consent. We may transfer, assign, or delegate this Agreement and our rights and obligations without consent.

12.2 - Choice of Law. This Agreement are governed by and will be construed under the Federal Arbitration Act, applicable federal law, and the laws of the State of California, without regard to the conflicts of laws provisions thereof.

12.3 - Arbitration Agreement. Please read the following ARBITRATION AGREEMENT carefully because it requires you to arbitrate certain disputes and claims with Modular and limits the manner in which you can seek relief from Modular. Both you and Modular acknowledge and agree that for the purposes of any dispute arising out of or relating to the subject matter of this Agreement, Modular's officers, directors, employees and independent contractors (âRepresentativesâ) are third-party beneficiaries of this Agreement, and that upon your acceptance of this Agreement, Representatives will have the right (and will be deemed to have accepted the right) to enforce this Agreement against you as the third-party beneficiary hereof.

A) Arbitration Rules; Applicability of Arbitration Agreement. You and Modular shall use best efforts to settle any dispute, claim, question, or disagreement arising out of or relating to the subject matter of this Agreement directly through good-faith negotiations, which shall be a precondition to either party initiating arbitration. If such negotiations do not resolve the dispute, it shall be finally settled by binding arbitration in San Mateo County, California. The arbitration will proceed in the English language, in accordance with the JAMS Streamlined Arbitration Rules and Procedures (the âRulesâ) then in effect, by one commercial arbitrator with substantial experience in resolving intellectual property and commercial contract disputes. The arbitrator shall be selected from the appropriate list of JAMS arbitrators in accordance with such Rules. Judgment upon the award rendered by such arbitrator may be entered in any court of competent jurisdiction.

B) Costs of Arbitration. The Rules will govern payment of all arbitration fees. Modular will pay all arbitration fees for claims less than seventy-five thousand ($75,000) dollars. Modular will not seek its attorneysâ fees and costs in arbitration unless the arbitrator determines that your claim is frivolous.Â

C) Waiver of Jury Trial. YOU AND MODULAR WAIVE ANY CONSTITUTIONAL AND STATUTORY RIGHTS TO GO TO COURT AND HAVE A TRIAL IN FRONT OF A JUDGE OR JURY. You and Modular are instead choosing to have claims and disputes resolved by arbitration. Arbitration procedures are typically more limited, more efficient, and less costly than rules applicable in court and are subject to very limited review by a court. In any litigation between you and Modular over whether to vacate or enforce an arbitration award, YOU AND MODULAR WAIVE ALL RIGHTS TO A JURY TRIAL, and elect instead to have the dispute be resolved by a judge.

D) Waiver of Class or Consolidated Actions. ALL CLAIMS AND DISPUTES WITHIN THE SCOPE OF THIS ARBITRATION AGREEMENT MUST BE ARBITRATED OR LITIGATED ON AN INDIVIDUAL BASIS AND NOT ON A CLASS BASIS. CLAIMS OF MORE THAN ONE USER CANNOT BE ARBITRATED OR LITIGATED JOINTLY OR CONSOLIDATED WITH THOSE OF ANY OTHER USER. If however, this waiver of class or consolidated actions is deemed invalid or unenforceable, neither you nor Modular is entitled to arbitration; instead all claims and disputes will be resolved in a court.

E)Â Opt-out. You have the right to opt out of the provisions of this Section by sending written notice of your decision to opt out to the following address: Modular, Inc., 228 Hamilton Ave, Palo Alto, CA 94301, USA, postmarked within thirty (30) days of first accepting this Agreement. You must include (i) your name and residence address, (ii) the email address and/or telephone number associated with your Account, and (iii) a clear statement that you want to opt out of this Agreementâs arbitration agreement.

F) Exclusive Venue. If you send the opt-out notice in (e), and/or in any circumstances where the foregoing arbitration agreement permits either you or Modular to litigate any dispute arising out of or relating to the subject matter of this Agreement in court, then the foregoing arbitration agreement will not apply to either party, and both you and Modular agree that any judicial proceeding (other than small claims actions) will be brought in the state or federal courts located in, respectively, San Mateo County, California, or the federal district in which that county falls.

G) Severability. If the prohibition against class actions and other claims brought on behalf of third parties contained above is found to be unenforceable, then all of the preceding language in this Arbitration Agreement section will be null and void. This arbitration agreement will survive the termination of your relationship with Modular.

12.4 - Miscellaneous. The failure of Modular to exercise, in any way, any right herein shall not be deemed a waiver of any further rights hereunder. If any provision of this Agreement is found to be unenforceable or invalid, that provision will be limited or eliminated, to the minimum extent necessary, so that this Agreement shall otherwise remain in full force and effect and enforceable. You and Modular agree that this Agreement is the complete and exclusive statement of the mutual understanding between you and Modular, and that the terms contained herein supersede and cancel all previous written and oral agreements, communications and other understandings relating to the subject matter of this Agreement. Except for changes by us as described here, no other amendment or modification of these Terms will be effective unless in writing and signed by both you and Modular. You hereby acknowledge and agree that you are not an employee, agent, partner, or joint venturer of Modular, and you do not have any authority of any kind to bind Modular in any respect whatsoever.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/legal/privacy

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# Privacy Policy

Last Updated: May 2nd, 2023

At Modular, we take your privacy seriously. Please read this Privacy Policy to learn how we treat your personal data. By using or accessing our Platform (as defined in the Terms) or our website, (collectively the âServicesâ) in any manner, you acknowledge that you accept the practices and policies outlined below, and you hereby consent that we will collect, use and share your information as described in this Privacy Policy.Â Remember that your use of Modular's Services is at all times subject to our Terms of Use (https://www.modular.com/terms)(collectively the âTermsâ). The Terms incorporate this Privacy Policy by reference. Any terms we use in this Privacy Policy without defining them have the definitions given to them in the Terms. Weâre constantly trying to improve our Services, so we may need to change this Privacy Policy from time to time, but we will alert you to any such changes by placing a notice on the Modular website, by sending you an email and/or by some other means. Please note that if youâve opted not to receive legal notice emails from us (or you havenât provided us with your email address), those legal notices will still govern your use of the Services, and you are still responsible for reading and understanding them. If you use the Services after any changes to the Privacy Policy have been posted, that means you agree to all of the changes. Use of information we collect is subject to the Privacy Policy in effect at the time such information is collected.You may print a copy of this Privacy Policy by clicking print.â

##### What this Privacy Policy Covers

This Privacy Policy covers how we treat Personal Data that we gather when you access or use our Services. âPersonal Dataâ means any information that identifies or relates to a particular individual and also includes information referred to as âpersonally identifiable informationâ or âpersonal informationâ under applicable data privacy laws, rules or regulations. This Privacy Policy does not cover the practices of companies we donât own or control or people we donât manage. If you are a User of the Platform, note that the âProfessional and Employment Related Dataâ categories of Personal Data collected and shared pursuant to this Privacy Policy are applicable only to users of the website who apply for open job positions with us.â

### Personal Data

##### Categories of Personal Data We Collect

This chart details the categories of Personal Data that we collect and have collected over the past 12 months:

##### Categories of Sources of Personal Data

This chart details the categories of Personal Data that we collect and have collected over the past 12 months:

We collect Personal Data about you from the following categories of sources:

###### 1. You

- When you provide such information directly to us. - When you create an account or use our interactive tools and Services. When you voluntarily provide information in free-form text boxes through the Services or through responses to surveys or questionnaires. When you send us an email or otherwise contact us.Â

- When you use the Services and such information is collected automatically. - Through Cookies (defined in the âTracking Tools and Opt-Outâ section below). If you use a location-enabled browser, we may receive information about your location. If you download and install certain applications and software we make available, we may receive and collect information transmitted from your computing device for the purpose of providing you the relevant Services, such as information regarding when you are logged on and available to receive updates or alert notices.

###### 2. Third Parties

- Vendors - We may use analytics providers to analyze how you interact and engage with the Services, or third parties may help us provide you with customer support. We may use vendors to obtain information to generate leads and create user profiles.

- Social Networks - If you provide your social network account credentials to us or otherwise sign in to the Services through a third-party site or service, some content and/or information in those accounts may be transmitted into your account with us.

##### Our Commercial or Business Purposes for Collecting or Disclosing Personal Data

###### 1. Providing, Customizing and Improving the Services

- Creating and managing your account or other user profiles.

- Processing orders or other transactions; billing.

- Providing you with the products, services or information you request.

- Meeting or fulfilling the reason you provided the information to us.

- Providing support and assistance for the Services.

- Improving the Services, including testing, research, internal analytics and product development.

- Personalizing the Services, website content and communications based on your preferences.

- Doing fraud protection, security and debugging.

- Carrying out other business purposes stated when collecting your Personal Data or as otherwise set forth in applicable data privacy laws, such as the California Consumer Privacy Act, as amended by the California Privacy Rights Act of 2020 (the âCPRAâ).

###### 2. Marketing the Services

Marketing and selling the Services.

###### 3. Corresponding with You

Responding to correspondence that we receive from you, contacting you when necessary or requested, and sending you information about Modular or the Services. Sending emails and other communications according to your preferences or that display content that we think will interest you.

###### 4. Meeting Legal Requirements and Enforcing Legal Terms

Fulfilling our legal obligations under applicable law, regulation, court order or other legal process, such as preventing, detecting and investigating security incidents and potentially illegal or prohibited activities. Protecting the rights, property or safety of you, Modular or another party. Enforcing any agreements with you. Responding to claims that any posting or other content violates third-party rights. Resolving disputes.

We will not collect additional categories of Personal Data or use the Personal Data we collected for materially different, unrelated or incompatible purposes without providing you notice.â

##### How We Share or Disclose Your Personal Data

We disclose your Personal Data to the categories of service providers and other parties listed in this section. Depending on state laws that may be applicable to you, some of these disclosures may constitute a âsaleâ of your Personal Data. For more information, please refer to the state-specific sections below.

- Service Providers. These parties help us provide the Services or perform business functions on our behalf. They include: Hosting, technology and communication providers. Security and fraud prevention consultants. Payment processors.

- Analytics Partners. These parties provide analytics on web traffic or usage of the Services. They include companies that track how users found or were referred to the Services. Companies that track how users interact with the Services.

- Advertising Partners.Â

- Parties You Authorize, Access or Authenticate Third parties you access through the services. Social media services. Other users.

###### Legal Obligations

We may share any Personal Data that we collect with third parties in conjunction with any of the activities set forth under âMeeting Legal Requirements and Enforcing Legal Termsâ in the âOur Commercial or Business Purposes for Collecting Personal Dataâ section above.Â

###### Business Transfers

All of your Personal Data that we collect may be transferred to a third party if we undergo a merger, acquisition, bankruptcy or other transaction in which that third party assumes control of our business (in whole or in part). Should one of these events occur, we will make reasonable efforts to notify you before your information becomes subject to different privacy and security policies and practices.

###### Data that is Not Personal Data

We may create aggregated, de-identified or anonymized data from the Personal Data we collect, including by removing information that makes the data personally identifiable to a particular user. We may use such aggregated, de-identified or anonymized data and share it with third parties for our lawful business purposes, including to analyze, build and improve the Services and promote our business, provided that we will not share such data in a manner that could identify you.Â Â

##### Tracking Tools and Opt-Out

The Services use cookies and similar technologies such as pixel tags, web beacons, clear GIFs and JavaScript (collectively, âCookiesâ) to enable our servers to recognize your web browser, tell us how and when you visit and use our Services, analyze trends, learn about our user base and operate and improve our Services. Cookies are small pieces of dataâ usually text files â placed on your computer, tablet, phone or similar device when you use that device to access our Services. We may also supplement the information we collect from you with information received from third parties, including third parties that have placed their own Cookies on your device(s). Please note that because of our use of Cookies, the Services do not support âDo Not Trackâ requests sent from a browser at this time.

We use the following types of Cookies:

- Essential Cookies. Essential Cookies are required for providing you with features or services that you have requested. For example, certain Cookies enable you to log into secure areas of our Services. Disabling these Cookies may make certain features and services unavailable.

- Functional Cookies. Functional Cookies are used to record your choices and settings regarding our Services, maintain your preferences over time and recognize you when you return to our Services. These Cookies help us to personalize our content for you, greet you by name and remember your preferences (for example, your choice of language or region).

- Performance/Analytical Cookies. Performance/Analytical Cookies allow us to understand how visitors use our Services. They do this by collecting information about the number of visitors to the Services, what pages visitors view on our Services and how long visitors are viewing pages on the Services. Performance/Analytical Cookies also help us measure the performance of our advertising campaigns in order to help us improve our campaigns and the Servicesâ content for those who engage with our advertising. For example, Google LLC (âGoogleâ) uses cookies in connection with its Google Analytics services. Googleâs ability to use and share information collected by Google Analytics about your visits to the Services is subject to the Google Analytics Terms of Service and the Google Privacy Policy. You have the option to opt-out of Googleâs use of Cookies by visiting the Google advertising opt-out page at www.google.com/privacy_ads.html or the Google Analytics Opt-out Browser Add-on at https://tools.google.com/dlpage/gaoptout/.

- Retargeting/Advertising Cookies. Retargeting/Advertising Cookies collect data about your online activity and identify your interests so that we can provide advertising that we believe is relevant to you.

You can decide whether or not to accept Cookies through your internet browserâs settings. Most browsers have an option for turning off the Cookie feature, which will prevent your browser from accepting new Cookies, as well as (depending on the sophistication of your browser software) allow you to decide on acceptance of each new Cookie in a variety of ways. You can also delete all Cookies that are already on your device. If you do this, however, you may have to manually adjust some preferences every time you visit our website and some of the Services and functionalities may not work.Â

To explore what Cookie settings are available to you or to modify your Cookies preferences, look in the âpreferencesâ or âoptionsâ section of your browserâs menu. To find out more information about Cookies, including information about how to manage and delete Cookies, please visit http://www.allaboutcookies.org/ or https://ico.org.uk/for-the-public/online/cookies/ if you are located in the European Union.

##### Data SecurityÂ

We seek to protect your Personal Data from unauthorized access, use and disclosure using appropriate physical, technical, organizational and administrative security measures based on the type of Personal Data and how we are processing that data. You should also help protect your data by appropriately selecting and protecting your password and/or other sign-on mechanism; limiting access to your computer or device and browser; and signing off after you have finished accessing your account. Although we work to protect the security of your account and other data that we hold in our records, please be aware that no method of transmitting data over the internet or storing data is completely secure.

##### Data Retention

We retain Personal Data about you for as long as you have an open account with us or as otherwise necessary to provide you with our Services or to perform our business or commercial purposes for collecting your Personal Data. When establishing a retention period for specific categories of data, we consider who we collected the data from, our need for the Personal Data, why we collected the Personal Data, and the sensitivity of the Personal Data. In some cases we retain Personal Data for longer, if doing so is necessary to comply with our legal obligations, resolve disputes or collect fees owed, or is otherwise permitted or required by applicable law, rule or regulation. We may further retain information in an anonymous or aggregated form where that information would not identify you personally.

##### Personal Data of Children

As noted in the Terms of Use, we do not knowingly collect or solicit Personal Data about children under 18 years of age; if you are a child under the age of 18, please do not attempt to register for or otherwise use the Services or send us any Personal Data. If we learn we have collected Personal Data from a child under 18 years of age, we will delete that information as quickly as possible. If you believe that a child under 18 years of age may have provided Personal Data to us, please contact us at hello@modular.com.

##### State Resident Rights

###### California

If you are a California resident, you have the rights set forth in this section. Please see the âExercising Your Rightsâ section below for instructions regarding how to exercise these rights. Please note that we may process Personal Data of our customersâ end users or employees in connection with our provision of certain services to our customers. If we are processing your Personal Data as a service provider, you should contact the entity that collected your Personal Data in the first instance to address your rights with respect to such data.

If there are any conflicts between this section and any other provision of this Privacy Policy and you are a California resident, the portion that is more protective of Personal Data shall control to the extent of such conflict. If you have any questions about this section or whether any of the following rights apply to you, please contact us at hello@modular.com.

###### Access

You have the right to request certain information about our collection and use of your Personal Data over the past 12 months. In response, we will provide you with the following information:

- The categories of Personal Data that we have collected about you.

- The categories of sources from which that Personal Data was collected.

- The business or commercial purpose for collecting or selling your Personal Data.

- The categories of third parties with whom we have shared your Personal Data.

- The specific pieces of Personal Data that we have collected about you.

If we have disclosed your Personal Data to any third parties for a business purpose over the past 12 months, we will identify the categories of Personal Data shared with each category of third party recipient. If we have sold your Personal Data over the past 12 months, we will identify the categories of Personal Data sold to each category of third party recipient.

###### Deletion

You have the right to request that we delete the Personal Data that we have collected about you. Under the CPRA, this right is subject to certain exceptions: for example, we may need to retain your Personal Data to provide you with the Services or complete a transaction or other action you have requested, or deletion may require disproportional effort. If your deletion request is subject to one of these exceptions, we may deny your deletion request.Â

###### CorrectionÂ

You have the right to request that we correct any inaccurate Personal Data we have collected about you. Under the CPRA, this right is subject to certain exceptions: for example, if we decide, based on the totality of circumstances related to your Personal Data, that such data is correct. If your correction request is subject to one of these exceptions, we may deny your request.Â

###### Exercising Your Rights

To exercise the rights described above, you or your Authorized Agent (defined below) must send us a request that (1) provides sufficient information to allow us to verify that you are the person about whom we have collected Personal Data, and (2) describes your request in sufficient detail to allow us to understand, evaluate and respond to it. Each request that meets both of these criteria will be considered a âValid Request.â We may not respond to requests that do not meet these criteria. We will only use Personal Data provided in a Valid Request to verify your identity and complete your request. You do not need an account to submit a Valid Request.

We will work to respond to your Valid Request within 45 days of receipt. We will not charge you a fee for making a Valid Request unless your Valid Request(s) is excessive, repetitive or manifestly unfounded. If we determine that your Valid Request warrants a fee, we will notify you of the fee and explain that decision before completing your request.

You may submit a Valid Request using the following methods:

- Email us at: hello@modular.com

- Submit a form at this address: https://www.modular.com/contact/us

You may also authorize an agent (an âAuthorized Agentâ) to exercise your rights on your behalf. To do this, you must provide your Authorized Agent with written permission to exercise your rights on your behalf, and we may request a copy of this written permission from your Authorized Agent when they make a request on your behalf.

###### Personal Data Sales Opt-Out and Opt-In

We will not sell your Personal Data, and have not done so over the last 12 months. To our knowledge, we do not sell the Personal Data of minors under 18 years of age.

We Will Not Discriminate Against You for Exercising Your Rights Under the CPRA

We will not discriminate against you for exercising your rights under the CPRA. We will not deny you our services, charge you different prices or rates, or provide you a lower quality of goods and services if you exercise your rights under the CPRA. However, we may offer different tiers of our Services as allowed by applicable data privacy laws (including the CPRA) with varying prices, rates or levels of quality of the goods or services you receive related to the value of Personal Data that we receive from you.Â

Under California Civil Code Sections 1798.83-1798.84, California residents are entitled to contact us to prevent disclosure of Personal Data to third parties for such third partiesâ direct marketing purposes; in order to submit such a request, please contact us at hello@modular.com. However, we do not sell your Personal Data or have plans to do so.Â

###### Nevada Resident Rights

If you are a resident of Nevada, you have the right to opt-out of the sale of certain Personal Data to third parties who intend to license or sell that Personal Data. You can exercise this right by contacting us at hello@modular.com with the subject line âNevada Do Not Sell Requestâ and providing us with your name and the email address associated with your account. However, we do not sell your Personal Data nor have plans to do so.Â

###### Virginia Resident Rights

If you are a Virginia resident, you have the rights set forth under the Virginia Consumer Data Protection Act (âVCDPAâ). Please see the âExercising Your Rightsâ section below for instructions regarding how to exercise these rights. Please note that we may process Personal Data of our customersâ end users or employees in connection with our provision of certain services to our customers. If we are processing your Personal Data as a service provider, you should contact the entity that collected your Personal Data in the first instance to address your rights with respect to such data. Additionally, please note that these rights are subject to certain conditions and exceptions under applicable law, which may permit or require us to deny your request.

If there are any conflicts between this section and any other provision of this Privacy Policy and you are a Virginia resident, the portion that is more protective of Personal Data shall control to the extent of such conflict. If you have any questions about this section or whether any of the following rights apply to you, please contact us at hello@modular.com.Â

###### Access

You have the right to request confirmation of whether or not we are processing your Personal Data and to access your Personal Data.Â

###### Correction

You have the right to correct inaccuracies in your Personal Data, to the extent such correction is appropriate in consideration of the nature of such data and our purposes of processing your Personal Data.Â

###### Portability

You have the right to request a copy of your Personal Data in a machine-readable format, to the extent technically feasible.Â

##### European Union and United Kingdom Data Subject Rights

###### EU and UK Residents

If you are a resident of the European Union (âEUâ), United Kingdom (âUKâ), Lichtenstein, Norway or Iceland, you may have additional rights under the EU or UK General Data Protection Regulation (the âGDPRâ) with respect to your Personal Data, as outlined below.Â Â

For this section, we use the terms âPersonal Dataâ and âprocessingâ as they are defined in the GDPR, but âPersonal Dataâ generally means information that can be used to individually identify a person, and âprocessingâ generally covers actions that can be performed in connection with data such as collection, use, storage and disclosure. Modular will be the controller of your Personal Data processed in connection with the Services.

If there are any conflicts between this this section and any other provision of this Privacy Policy, the policy or portion that is more protective of Personal Data shall control to the extent of such conflict. If you have any questions about this section or whether any of the following applies to you, please contact us at hello@modular.com.Â

###### Personal Data We Collect

The âCategories of Personal Data We Collectâ section above details the Personal Data that we collect from you.

###### Personal Data Use and Processing Grounds

The âOur Commercial or Business Purposes for Collecting Personal Dataâ section above explains how we use your Personal Data.

We will only process your Personal Data if we have a lawful basis for doing so. Lawful bases for processing include consent, contractual necessity and our âlegitimate interestsâ or the legitimate interest of others, as further described below.

- Contractual Necessity:Â  We process the following categories of Personal Data as a matter of âcontractual necessityâ, meaning that we need to process the data to perform under our Terms of Use with you, which enables us to provide you with the Services. When we process data due to contractual necessity, failure to provide such Personal Data will result in your inability to use some or all portions of the Services that require such data.Â

- Profile or contact data

- Payment Data

- Legitimate Interest:Â  We process the following categories of Personal Data when we believe it furthers the legitimate interest of us or third parties Device data, Web analytics data, Geolocation Data, Professional Data, We may also de-identify or anonymize Personal Data to further our legitimate interests.Â

Examples of these legitimate interests include (as described in more detail above):

- Providing, customizing and improving the Services.

- Marketing the Services.

- Corresponding with you.

- Meeting legal requirements and enforcing legal terms.

- Completing corporate transactions.Â

- Consent:Â  In some cases, we process Personal Data based on the consent you expressly grant to us at the time we collect such data. When we process Personal Data based on your consent, it will be expressly indicated to you at the point and time of collection.Â

- Other Processing Grounds:Â  From time to time we may also need to process Personal Data to comply with a legal obligation, if it is necessary to protect the vital interests of you or other data subjects, or if it is necessary for a task carried out in the public interest.

###### Sharing Personal Data

The âHow We Share Your Personal Dataâ section above details how we share your Personal Data with third parties.Â

###### EU Data Subject Rights

You have certain rights with respect to your Personal Data, including those set forth below. For more information about these rights, or to submit a request, please email us at hello@modular.com. Please note that in some circumstances, we may not be able to fully comply with your request, such as if it is frivolous or extremely impractical, if it jeopardizes the rights of others, or if it is not required by law, but in those circumstances, we will still respond to notify you of such a decision. In some cases, we may also need you to provide us with additional information, which may include Personal Data, if necessary to verify your identity and the nature of your request.Â Â

- Access:Â  You can request more information about the Personal Data we hold about you and request a copy of such Personal Data. You can also access certain of your Personal Data by logging into your account.

- Rectification:Â  If you believe that any Personal Data we are holding about you is incorrect or incomplete, you can request that we correct or supplement such data. You can also correct some of this information directly by logging on to your account.

- Erasure:Â  You can request that we erase some or all of your Personal Data from our systems.Â Â

- Withdrawal of Consent:Â  If we are processing your Personal Data based on your consent (as indicated at the time of collection of such data), you have the right to withdraw your consent at any time. Please note, however, that if you exercise this right, you may have to then provide express consent on a case-by-case basis for the use or disclosure of certain of your Personal Data, if such use or disclosure is necessary to enable you to utilize some or all of our Services.Â

- Portability:Â  You can ask for a copy of your Personal Data in a machine-readable format. You can also request that we transmit the data to another controller where technically feasible.Â

- Objection:Â  You can contact us to let us know that you object to the further use or disclosure of your Personal Data for certain purposes, such as for direct marketing purposes.Â

- Restriction of Processing:Â  You can ask us to restrict further processing of your Personal Data.

- Right to File Complaint:Â  You have the right to lodge a complaint about Modular's practices with respect to your Personal Data with the supervisory authority of your country or EU Member State. A list of Supervisory Authorities is available here: https://edpb.europa.eu/about-edpb/board/members_en.

###### Transfers of Personal Data

The Services are hosted and operated in the United States (âU.S.â) through Modular and its service providers, and if you do not reside in the U.S., laws in the U.S. may differ from the laws where you reside. By using the Services, you acknowledge that any Personal Data about you, regardless of whether provided by you or obtained from a third party, is being provided to Modular in the U.S. and will be hosted on U.S. servers, and you authorize Modular to transfer, store and process your information to and in the U.S., and possibly other countries. You hereby consent to the transfer of your data to the U.S. pursuant to EU-U.S. Privacy Shield Frameworks, respectively, the details of which are further set forth below.Â

While the Privacy Shield is no longer a valid basis on which Modular may rely to transfer Personal Data from the EU to the U.S. pursuant to the GDPR, Modular continues to comply with the EU-U.S. Privacy Shield Framework as set forth by the U.S. Department of Commerce regarding the collection, use and retention of Personal Data from EU member countries transferred to the U.S. pursuant to Privacy Shield. Modular has certified that it adheres to the Privacy Shield Principles with respect to such data. If there is any conflict between the policies in this Privacy Policy and data subject rights under the Privacy Shield Principles, the Privacy Shield Principles shall govern. To learn more about the Privacy Shield program, and to view our certification page, please visit https://www.privacyshield.gov/. Modular will transfer Personal Data from the EU to the U.S. pursuant to GDPR adhering to the model clauses.Â

In certain situations, we may be required to disclose Personal Data in response to lawful requests by public authorities, including to meet national security or law enforcement requirements.

##### Changes to this Privacy Policy

###### Contact Information:

If you have any questions or comments about this Privacy Policy, the ways in which we collect and use your Personal Data or your choices and rights regarding such collection and use, please do not hesitate to contact us at:

- www.modular.com

- hello@modular.com

- www.modular.com/contact

â

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/legal/aup

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# Acceptable AIÂ Use Policy

Last Modified: Feb 29th, 2024

This Acceptable AI Use Policy (this âPolicyâ) forms part of both Modularâs Terms of Use (https://modular.com/legal/terms), and governs User use of the Max Platform. This Policy outlines certain restricted activities in which Users may not engage while using the Max Platform. Modular reserves the right to issue warnings, suspend, and/or terminate User Accounts for any suspected or actual violation(s) of or failure(s) to comply with the guidelines set forth in this Policy.

Modular remains committed to building a community where Users can safely interact with and build on top of our Max Platform in meaningful, honest, and productive ways. This Policy is in place to ensure both User safety and end-user of User-Generated Content and Application safety, while ensuring responsible use of the Max Platform.

Users are reminded that generative artificial intelligence tools and models are capable of producing factually inaccurate, harmful, or biased information. Modularâs goal is to make responsible artificial products available on a platform that emphasizes responsible use of artificial tools for a variety of use cases. This Policy is designed with those principles in mind.

##### 1. Compliance with Laws and Regulations

Users shall use the Max Platform in compliance with all applicable laws, regulations, and all of the guidelines set forth herein.

##### 2. Intellectual Property, Authenticity, and Private Information

We do not allow content or activity on the Max Platform that:

- attempts to reverse engineer, scrape, or otherwise utilize the Max Platform for any competitive purposes, including but not limited to discovering Modular IP that constitutes Confidential Information for use in a competitive product;Â

- infringes any intellectual property, privacy, or proprietary right of any party, including patent, trademark, trade secret, copyright, right of publicity, personal information or other rights;

- unlawfully shares unauthorized product licensing keys, software for generating unauthorized product licensing keys, or software for bypassing checks for product licensing keys, including extension of a free license beyond its trial period; or

- impersonates any person or entity, including any of our employees or representatives, including through false association with Modular, or by fraudulently misrepresenting oneâs identity or the Max Platformâs purpose.

##### 3. Spam, Fraudulent, Dishonest, Illegal, and Inauthentic Activity

We do not allow User Generated Content, use of, or activity on the Max Platform that comprises of:

- automated excessive bulk activity and coordinated inauthentic activity, such as

- spamming

- cryptocurrency mining;

- bulk distribution of promotions and advertising;

- inauthentic interactions, such as fake accounts and automated inauthentic activity;

- creation of or participation in secondary markets for the purpose of the proliferation of inauthentic activity;

- using the Max Platform for propagating abuse on other platforms;

- phishing or attempted phishing; or

- using the Max Platform for any form of excessive automated bulk activity, to place undue burden on our servers through automated means, or to relay any form of unsolicited advertising or solicitation through our servers, such as get-rich-quick schemes.

Users may not use the Max Platform to:

- Engage in any illegal activity;

- Provide instructions on how to create or facilitate the exchange of illegal substances or goods;

- Encourage or provide instructions on how to engage in or facilitate illegal services such as human trafficking or prostitution;

- Design, market, help distribute or utilize weapons, explosives, dangerous materials or other systems designed to cause harm to or loss of human life;

- Provide instructions on how to commit or facilitate any type of crime;

- Gamble or bet on sports;

- Engage in multi-level marketing or pyramid schemes; or

- Plagiarize or engage in other forms of academic dishonesty.

##### 4. Site Access and Safety

We do not allow content or activity on MAXÂ Platform that:

- directly supports unlawful active attack or malware campaigns that are causing technical harms, such as using the Max Platform to deliver malicious executables or as attack infrastructure, for example by organizing denial of Max Platform attacks or managing command and control servers, with no implicit or explicit dual-use purpose prior to the abuse occurring; or

- uses our servers to disrupt or to attempt to disrupt, or to gain or to attempt to gain unauthorized access to, any platform, device, data, account or network.

##### 5. Platforms Usage Limits

Users will not reproduce, duplicate, copy, sell, resell, scrape, re-use or exploit any portion of the Max Platform, use of the Max Platform, or access to the Max Platform without Modularâs express written permission.

##### 6. Information Usage Restrictions

Users may not use information from the Max Platform (whether scraped, downloaded through our website, or obtained otherwise) for spamming purposes, including for the purposes of sending unsolicited emails to users or selling personal information, such as to recruiters, headhunters, and job boards.

##### 7. Advertising

While we understand that Users may want to promote their respective User Generated Content by posting supporters' names or logos in its account, the primary focus of the User Generated Content posted to the Max Platform should not be advertising or promotional marketing. Users may include static images, links, and promotional text in the README documents or project description sections associated with its Account(s), but they must be related to the AI Algorithms Users and Developers are sharing on the Max Platform. Users may not advertise by posting monetized or excessive bulk content.

Users may not promote or distribute content or activity that is illegal or otherwise prohibited by the applicable Terms of Use by which they are bound or this Policy, including excessive automated bulk activity (for example, spamming), get-rich-quick schemes, and misrepresentation or deception related to its promotion.If a User decide to post any promotional materials in its Account, User is solely responsible for complying with all applicable laws and regulations, including without limitation the U.S. Federal Trade Commission's Guidelines on Endorsements and Testimonials. Modular reserves the right to remove any promotional materials or advertisements that, in our sole discretion, violate any Modular terms.

##### 8. Prohibited Business Use CasesÂ

In addition to the above use cases, we prohibit businesses from using the Max Platform for any of the following:

- Creating targeted campaigns to influence the outcome of elections or referendums; political advocacy or lobbying;

- Searching for or gathering information on an individual or group in order to track, target or report on their identity, including using the product for facial recognition, covert tracking, or predictive policing;

- Utilizing any SDK to assign scores or ratings to individuals based on an assessment of their trustworthiness or social behavior;

- Eligibility for parole or sentencing decisions;

- Making automated decisions about the employability of individuals or other employment determinations or decisions regarding eligibility for housing, including leases and home loans;

- Except for the following permitted applications by U.S. law enforcement organizations:

- Back office uses including call center support, document summarization, and accounting;

- Analysis of data for the location of missing persons and other applications, provided that such applications do not otherwise violate or impair the liberty, civil liberties, or human rights of natural persons.

##### 9. Additional Requirements for Businesses

If your business is using or deploying our tools and services as part of providing legal, medical, or financial advice to consumers, we require that you implement the additional safety measures listed below:

- Human-in-the-loop: any content that is provided to your consumers must be reviewed by a qualified professional in that field prior to dissemination. Your business is responsible for the accuracy and appropriateness of that information.â

- Disclosure: you must disclose to your end users or customers that you are using our services to help inform your decisions or recommendations

##### 10. Privacy

Misuse of Personal Information is prohibited. Any person or entity collecting data from the Max Platform must comply with the Modular Privacy Policy (https://www.modular.com/legal/privacy). If a User collects any Personal Information from the Max Platform, that User agrees it will only use that Personal Information for the purpose for which the respective User to whom the Personal Information pertains has authorized its use. User agrees that it will reasonably secure any Personal Information it has gathered from the Platform, and User will respond promptly to complaints, removal requests, and "do not contact" requests from us or other Users (to the extent applicable).

##### 11. User Safety

We do not allow conduct, Use Generated Content, activity, or use of the Max Platform that:

- is unlawful or promotes unlawful activities;

- is sexually obscene or relates to sexual exploitation or abuse, including of minors;

- is libelous, defamatory, or fraudulent;

- is discriminatory or abusive toward any individual or group;

- is false, inaccurate, or intentionally deceptive information and likely to adversely affect the public interest (including health, safety, election integrity, and civic participation);

- harasses or abuses another individual or group, including our employees, officers, and agents, or other Users;

- threatens or incites violence toward any individual or group, especially on the basis of who they are;

- gratuitously depicts or glorifies violence, including violent images; or

- is off-topic, or interacts with Max Platform features in a way that significantly or repeatedly disrupts the experience of other Users.

##### 12. User Protection

User must not engage in activity that significantly harms other Users. Modular will interpret policies and resolve disputes in favor of protecting Users as a whole.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog/mojo-a-journey-to-68-000x-speedup-over-python-part-3

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

September 6, 2023

# Mojoð¥ - A journey to 68,000x speedup over Python - Part 3

Abdul Dakkak

We started this blog post series to describe how to write Mojoð¥ for the Mandelbrot set to achieve over 35,000x speedup over Python. To recap the optimizations so far, in part 1 we ported the code into Mojo to get around a 90x speedup, and then, in part 2 we vectorized and parallelized the code to get a 26,000x. This blog post continues this journey to show another performance technique that takes well beyond our promised 35,000x speedup goal.

â

Recall that our parallelism strategy in the previous blog post is that each CPU core gets an equal number of rows to process. Pictorially, the idea is that we can evaluate each set of rows independently and achieve speedup proportional to the number of cores.

â

### Load imbalance

However, the animation in Figure 2 is deceiving. Partitioning the work so that each thread worker gets a set of rows is fine when the work is the same across the rows, but thatâs not the case for the Mandelbrot set. Partitioning this way creates a load imbalance, since one pixel of the Mandelbrot set may finish after a single iteration and another pixel may use MAX_ITERS iterations. This means that the number of iterations per row is not equal, and therefore some threads will be sitting idle, because they finish the computation faster than other ones.

To demonstrate the imbalance, we plot the total number of iterations each row performs within the Mandelbrot set. As shown in the figure below, some rows require less than 1000 iterations before they escape whereas others require over 800,000 iterations.Â

If you partition in a way that each thread gets a sequential number of rows, what happens is that all threads will wait until the middle set of rows finishes (assigned to some core) before completion. There are multiple ways to alleviate this, but the easiest way is to over-partition. So, instead of each thread getting a set of rows divided equally amongst the cores, we have a work-pool and create a work item for each row. The threads pick items from the thread pool in round-robin fashion.

Thankfully, Mojo includes a high-performance concurrent runtime, so we do not have to create a thread pool or do the round-robin picking and execution ourselves. Mojoâs runtime includes advanced features to fully utilize many-core systems like this one.

fn compute_row(y:Int):
    let cy = min_y + h * scale_y

    @parameter
    fn compute_vector[simd_width:Int](w:Int):
        let cx = min_x + iota[DType.float64, simd_width]() * scale_x
        output.simd_store[simd_width](Index(h,w), 
                mandelbrot_kernel(ComplexSIMD[DType.float64, 
                                              simd_width](cx,cy))

    vectorize[num_ports * simd_width, compute_vector](width)

with Runtime(num_cores()) as rt:
    let partition_factor = 16 # Is autotuned.
    parallelize[compute_row](rt, height, partition_factor * num_cores())

â

We can evaluate the program as we over partition by 2, 4, 8, 16, and 32 factors. The results are shown below:

â

Effectively, we get a 2.3x speedup over the parallel version and 78x speedup over the vectorized implementation.Â An acute reader will ask whether itâs advantageous to partition even further â within a row. The answer is âmaybeâ if the row-size is large, but in our case it is 4096. Furthermore, within a row, pixels tend to be more correlated. This is advantageous for SIMD, since it means that work is not wasted within a vector lane.Â

### Why are we getting more than 35,000x speedup?

Why are we getting a 68,000x speedup instead of the advertised 35,000x speedup? In short, the benchmarking system is different. During launch, we evaluated on an AWS r7iz.metal-16xl 32-Core Intel Xeon Gold 6455B, but in this blog we evaluated on a GCP h3-standard-88 which uses an 88-Core Intel Xeon Platinum 8481C. The reason we do not use the r7iz.metal-16xl instance is because it is no longer available. But that said, we donât mind exceeding expectations!

A higher core count and faster clock speed generally improve Mandelbrot problem speedup, and this actually shows the benefit of Mojo. Computers are only getting faster and bigger â core count is only going to increase and so (to a lesser extent) will the clock frequency. In general, you can expect speedup to double over the sequential version when you double your core count, while increasing your clock speed will exacerbate the significance of interpretation overhead. Additionally, such hardware scaling does not require Mojo code to be changed. In fact, we did not optimize the program across the instance types, but we are still able to get a performance improvement.Â

Another question is why âwe are getting a 2x speedup over the r7iz instance and not a 2.7x?â The answer is that the problem size is too small to saturate the CPUs and we did not optimize for the h3-standard-88 instance. For example, hyper threading is beneficial in general for the Mandelbrot computation, but we were unable to enable hyper threading on the h3-standard-88 instance type.

### Performance recap

To summarize the optimization journey in the first 3 blog posts. In the first blog we first started by just taking our Python code and running it in Mojo, added some type annotations to the Mojo code, and performed algebraic simplifications. In the second blog post, we vectorizing the code, evolved that to utilize multiple ports, and then made the sequential implementation parallel to exercise all the cores on the system. Finally, in this blog post, we used oversubscription to resolve the load imbalance due to parallelism.Â During this journey we achieved 46x to 68,000x speedups over Python. As shown below.

â

Again, itâs hard to read such massive speedups. So, to better show the differences in log scale:

â

As shown below, our implementation achieves over 68,000x speedup over Python on this machine, and using oversubscription we are able to get 2x speedup over our previous parallel version.

### Conclusion

In this blog post we explained the final optimization necessary to achieve the 35,000x claim made during launch. In the process, we accidentally overachieved and got a 68,000x speedup!

Throughout these blog posts, we showed that Mojo affords you great performance while being portable and easy to reason about. Performance does not come for free, however, and thus one has to be cognizant of the underlying hardware and algorithms to be able to achieve it.Â

We employ the same principles when developing high performance numeric kernels for the Modular AI Engine. If you are interested in making algorithms fast and driving state-of-the-art AI infrastructure from the ground up, please consider joining Modularâs exceptional team by applying to our Kernel engineer role or other other roles available on our careers page.

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Developer

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Abdul Dakkak

AI Compiler Engineer

Expert in machine learning, compilers, programming languages, and accelerated computing. Before Modular, Abdul led the development of AI compilers for GPUs at Microsoft Research and the Mathematica Compiler at Wolfram Research. Abdul has developed open-source tools for accelerating real-world applications to optimize their performance across the hardware and software stack.



================================================================================

URL: https://www.modular.com/legal/max-mojo-license

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# MAX &Â Mojo Community License

The FAQ that accompanies this License is available on our Pricing page and plays an important part in providing guidance to our enterprises, developers and community. We view the guidance in our FAQ as binding, so users of our software should feel assured in relying on the answers provided there.

Last Modified: August 28th, 2024

These MAX Community License Terms (these "Terms") are entered into by and between Modular, Inc. ("Modular") and the individual or entity agreeing to these terms ("Licensee"), and form part of Modularâs Terms of Use. Capitalized terms used herein but not otherwise defined have the meanings set forth in the Terms of Use. By clicking the "Agree" button or by downloading, installing, copying, or otherwise using any software development kits made available by Modular as part of the Max Platform (each, the "SDK"), or otherwise developing software using Modularâs Mojo programming language, Licensee agrees to be bound by these Terms. If Licensee does not agree to these Terms, Licensee should not proceed with the download, installation, or use of the applicable SDK. If Licensee is an individual downloading the SDK on behalf of an entity or organization, Licensee represents and warrants that it has the requisite authority to bind that entity (and any applicable Affiliates) to these Terms.

##### 1. SDK AND DOCUMENTATION LICENSE GRANT

Subject to Licenseeâs compliance with these Terms, Modular hereby grants Licensee a non-exclusive, non-transferable license to use the SDK and accompanying Documentation (as defined below), as made available at https://docs.modular.com, solely for use in developed software applications ("Applications") that meet the Usage and Distribution Requirements specified in this Agreement. This license does not grant Licensee the right to distribute the SDK or any part thereof to third parties, except as expressly stated otherwise herein. For the avoidance of doubt, Licensee may under no circumstances redistribute the SDK itself outside of any Applications. Nothing herein shall be construed as a grant of a license or any other rights with respect to Modularâs trademarks, logos, or other branding for Mojo or any other Modular offerings (collectively, the âMarksâ). Licensee shall seek prior written consent for the use of the Marks in connection with its use of the SDK, and agrees and acknowledges that Modular is under no obligation to grant any such requests.

##### 2. MAX RESTRICTIONS AND USAGE

Licensee shall not:

(a) modify, adapt, translate, reverse engineer, decompile, or disassemble the SDK or any part thereof, or otherwise attempt to derive or gain access to SDK source code; (b) remove, alter, or obscure any proprietary notices, labels, or marks from the SDK; (c) use the SDK in an Application or standalone, or otherwise develop an Application in Mojo, for any Competitive Activity; or (d) use the SDK in any manner that violates any applicable laws, the Distribution Requirements, regulations, third-party rights, or other Modular terms or agreements by which Licensee is bound. Licensee acknowledges and understands that its use of the SDK may be subject to the collection of certain telemetry, usage, and other data which captures Licenseeâs interactions with and use of the SDK (collectively, the âUsage Dataâ).âCompetitive Activityâ means making commercially, publicly available software-as-a-service, platform-as-a-service, infrastructure-as-a-service, support services, or similar online or offline managed, cloud services for the primary purposes of providing artificial intelligence platform infrastructure to third parties without explicit written permission. For clarity, artificial intelligence platform infrastructure encapsulates:

(i) data processing, transformation, and querying services for artificial intelligence, (ii) artificial intelligence training or artificial intelligence inference services, (iii) hosting and/or compute services for the above, and (iv) data center services and/or similarly situated services offering MAX in connection with a broader service offering to third parties.For the avoidance of doubt, this provision will not apply âex-post-factoâ (âafter the factâ) to any software you develop that is not competitive at the time you develop it, but later on becomes competitive to Modular based on Modularâs new product offerings or pivots resulting in an expanded definition of Competitive Activity. Development of any internal or personal use-only software is not a Competitive Activity. Please see our pricing page (https://www.modular.com/pricing) for more information and examples on Competitive Activities.

##### 3. REDISTRIBUTABLE COMPONENTS & LIMITATIONS

Modular hereby grants Licensee the limited right to redistribute certain components of the SDK as part of Licensee's Applications, subject to the conditions and limitations set forth in this clause, including the Distribution Requirements. The redistributable components of the SDK ("Redistributable Components") are specified in the accompanying SDK documentation or materials provided by Modular (the âDocumentationâ). Licensee shall only redistribute the Redistributable Components as expressly permitted in these Terms and in any Documentation, in accordance with the Distribution Requirements.

âDistribution Requirementsâ means each of the following requirements:

(a) The Application must have material additional functionality, beyond the included portions of the SDK;(b) The distributable portions of the SDK shall only be accessed by the Application;(c) The terms under which Licensee distributes an Application must be consistent with the terms of this Agreement, including (without limitation) terms relating to the license grant and license restrictions and protection of Modularâs intellectual property rights;(d) Licensee shall not redistribute the Redistributable Components as standalone components, libraries, or tools in any way that would enable third parties to violate the terms of this License;(e) Licensee shall not modify, reverse engineer, decompile, disassemble, or attempt to derive the source code of the Redistributable Components, except to the extent expressly permitted by applicable law;(f) Licensee shall include all proprietary notices, labels, and marks provided by Modular in the Documentation (or elsewhere) in connection with the Redistributable Components in all copies of Applications that incorporate the Redistributable Components;(g) The text of this Agreement shall be conspicuously displayed in each original or modified copy of the SDK; and(h) The Application must only be run on hardware expressly supported by MAX. If Licensee wishes to run the Application on custom hardware it is developing or has developed, Licensee must contact Modular with a written request to secure appropriate rights to do so. Modular reserves the right to approve or deny such requests in its sole discretion.

Licensee agrees to notify Modular in writing of any known or suspected violation of the Distribution Requirements and to reasonably enforce the terms of its Application. If Licensee receives the SDK in original or modified form from any third party, Licensee agrees and accepts that the terms of this Agreement are still binding upon Licensee.

##### 4. OWNERSHIP &Â PERSONALÂ INFORMATION

Modular and its applicable licensors retain all right, title, and interest in and to the SDK (including any Redistributable Components) and Documentation, including all intellectual property rights therein. Licensee acknowledges and agrees that, other than as explicitly stated herein, nothing in these Terms grants it any rights to or ownership interest in the SDK or Documentation. As between the parties, Licensee acknowledges that Modular owns all right, title, and interest in and to the Usage Data and may use or exploit it for any purpose it deems fit, including but not limited to the purposes of improving the SDK or any other component or aspect of the Max Platform. Any collection, storage, or use of Licensee personal information is governed by Modularâs Privacy Policy (https://www.modular.com/legal/privacy), which Licensee hereby acknowledges upon acceptance of these Terms.

##### 5. SUPPORT AND UPDATES

Modular is not obligated to provide any support, patches, fixes or updates for the SDK. However, Modular may, at its sole discretion, provide updates, patches, fixes or support at any time.

##### 6. FEES

The community license variant of the SDK is provided free of charge. Licensee acknowledges that Modular may at any time change its provision of its community variant of the SDK for free or generally. In the event Modular does the foregoing, it will make commercially reasonable efforts to provide Licensee of advanced notice of its plans to either change its provision of the SDK or offer the applicable, commercial variant of the SDK for a fee to the contact information provided by Licensee to Modular.

##### 7. WARRANTY DISCLAIMER

THE SDK IS PROVIDED "AS IS" AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, MODULAR MAKES NO OTHER WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. LICENSEE ACKNOWLEDGES AND AGREES THAT THE USE OF THE SDK IS AT THEIR OWN RISK, AND MODULAR DOES NOT MAKE ANY REPRESENTATIONS OR WARRANTIES REGARDING THE ACCURACY, RELIABILITY, OR COMPLETENESS OF THE SDK, OR INFORMATION OBTAINED THROUGH THE SDK. MODULAR DOES NOT WARRANT OR GUARANTEE THAT THE SDK WILL MEET LICENSEE'S REQUIREMENTS OR EXPECTATIONS FOR ITS APPLICATIONS OR GENERALLY, OR THAT ANY ERRORS OR DEFECTS IN THE SDK WILL BE CORRECTED. FURTHERMORE, MODULAR DOES NOT WARRANT OR MAKE ANY REPRESENTATIONS REGARDING THE RESULTS THAT MAY BE OBTAINED FROM THE USE OF THE SDK OR THE SUITABILITY OF THE SDK FOR ANY PARTICULAR PURPOSE. MODULAR MAKES NO WARRANTIES OR REPRESENTATIONS THAT THE SDK WILL BE COMPATIBLE WITH LICENSEE'S SYSTEMS, SOFTWARE, OR APPLICATIONS, OR THAT THE SDK WILL BE SECURE FROM UNAUTHORIZED ACCESS, HACKING, OR OTHER POTENTIAL SECURITY THREATS. MODULAR DISCLAIMS ANY LIABILITY ARISING OUT OF OR IN CONNECTION WITH THE USE OF THE SDK. LICENSEE ASSUMES ALL RISKS AND RESPONSIBILITIES FOR USING THE SDK TO ACHIEVE THEIR INTENDED RESULTS AND FOR THE INSTALLATION, USE, AND RESULTS OBTAINED FROM THE SDK. ADDITIONALLY, VERSIONS OF THE SDK IDENTIFIED AS âPREVIEW VERSIONSâ MAY HAVE ADDITIONAL BUGS OR DESIGN FLAWS. MODULAR MAKES NO WARRANTIES ABOUT THE PREVIEW VERSIONS WHATSOEVER, AND LICENSEEâS USE OF THE SDK IN COMMERCIAL, PRODUCTION ENVIRONMENTS IN VIOLATION OF THIS AGREEMENT IS AT ITS OWN RISK.

##### 8. INDEMNIFICATION

Licensee shall defend, indemnify and hold harmless Modular, its affiliates and their respective officers, directors, employees, agents and representatives from any and all third party claims, damages, liabilities, costs and fees (including reasonable attorneysâ fees) arising from: (i) its material breach of these Terms; or (ii) an allegation that any Application violates, misappropriates, or infringes any third party intellectual, contractual or proprietary right.

##### 9. LIMITATION OF LIABILITY

TO THE EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL MODULAR BE RESPONSIBLE OR LIABLE WITH RESPECT TO ANY SUBJECT MATTER OF THIS AGREEMENT UNDER ANY CONTRACT, NEGLIGENCE, STRICT LIABILITY OR OTHER THEORY FOR: (I) LOSS OR INACCURACY OF DATA OR COST OF PROCUREMENT OF SUBSTITUTE GOODS, SERVICES, OR TECHNOLOGY; OR (II) ANY INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES INCLUDING, BUT NOT LIMITED TO LOSS OF REVENUES OR PROFITS; (III) ANY MATTER BEYOND ITS REASONABLE CONTROL; OR (IV) ANY AMOUNT IN THE AGGREGATE OVER $100. SOME JURISDICTIONS DO NOT ALLOW CERTAIN WARRANTY DISCLAIMERS OR LIMITATIONS ON LIABILITY. ONLY DISCLAIMERS OR LIMITATIONS THAT ARE LAWFUL IN THE APPLICABLE JURISDICTION WILL APPLY TO YOU, AND MODULARâS LIABILITY WILL BE LIMITED TO THE MAXIMUM EXTENT PERMITTED BY LAW.

##### 10. TERMINATION

These Terms may be terminated if the Licensee wants to terminate this Agreement by stopping use of the SDK. Modular may terminate this License if the Licensee fails to comply with any term in this Agreement, or if the Licensee commences, or participates, in any legal proceed against Modular. Upon termination, Licensee shall immediately cease all use of the SDK. These terms may be also discontinued by Modular if it decides to no longer provide any SDK, or if in Modular soles discretion, continued use is no longer commercially viable. Modular will attempt to provide written notice of termination to Licensee either through a posting on its website, or via the contact method for Licensee it has available. Upon any termination of this License all provisions survive except for the license grant provisions.

##### 11. EXPORTÂ CONTROL

Licensee shall comply with, and shall, at Modularâs request, demonstrate compliance with all applicable export laws, restrictions, and regulations of any United States or foreign agency or authority. Licensee shall not export or re-export, or allow the export or re-export of any product, technology or information it obtains pursuant to these Terms (including but not limited to the SDK) in violation of any such laws, embargoes, restrictions or regulations. Licensee shall obtain and bear all expenses relating to any licenses and/or exemptions required to comply with foregoing.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/legal/max

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# MAX &Â Mojo Community License

The FAQ that accompanies this License is available on our Pricing page and plays an important part in providing guidance to our enterprises, developers and community. We view the guidance in our FAQ as binding, so users of our software should feel assured in relying on the answers provided there.

Last Modified: August 28th, 2024

These MAX Community License Terms (these "Terms") are entered into by and between Modular, Inc. ("Modular") and the individual or entity agreeing to these terms ("Licensee"), and form part of Modularâs Terms of Use. Capitalized terms used herein but not otherwise defined have the meanings set forth in the Terms of Use. By clicking the "Agree" button or by downloading, installing, copying, or otherwise using any software development kits made available by Modular as part of the Max Platform (each, the "SDK"), or otherwise developing software using Modularâs Mojo programming language, Licensee agrees to be bound by these Terms. If Licensee does not agree to these Terms, Licensee should not proceed with the download, installation, or use of the applicable SDK. If Licensee is an individual downloading the SDK on behalf of an entity or organization, Licensee represents and warrants that it has the requisite authority to bind that entity (and any applicable Affiliates) to these Terms.

##### 1. SDK AND DOCUMENTATION LICENSE GRANT

Subject to Licenseeâs compliance with these Terms, Modular hereby grants Licensee a non-exclusive, non-transferable license to use the SDK and accompanying Documentation (as defined below), as made available at https://docs.modular.com, solely for use in developed software applications ("Applications") that meet the Usage and Distribution Requirements specified in this Agreement. This license does not grant Licensee the right to distribute the SDK or any part thereof to third parties, except as expressly stated otherwise herein. For the avoidance of doubt, Licensee may under no circumstances redistribute the SDK itself outside of any Applications. Nothing herein shall be construed as a grant of a license or any other rights with respect to Modularâs trademarks, logos, or other branding for Mojo or any other Modular offerings (collectively, the âMarksâ). Licensee shall seek prior written consent for the use of the Marks in connection with its use of the SDK, and agrees and acknowledges that Modular is under no obligation to grant any such requests.

##### 2. MAX RESTRICTIONS AND USAGE

Licensee shall not:

(a) modify, adapt, translate, reverse engineer, decompile, or disassemble the SDK or any part thereof, or otherwise attempt to derive or gain access to SDK source code; (b) remove, alter, or obscure any proprietary notices, labels, or marks from the SDK; (c) use the SDK in an Application or standalone, or otherwise develop an Application in Mojo, for any Competitive Activity; or (d) use the SDK in any manner that violates any applicable laws, the Distribution Requirements, regulations, third-party rights, or other Modular terms or agreements by which Licensee is bound. Licensee acknowledges and understands that its use of the SDK may be subject to the collection of certain telemetry, usage, and other data which captures Licenseeâs interactions with and use of the SDK (collectively, the âUsage Dataâ).âCompetitive Activityâ means making commercially, publicly available software-as-a-service, platform-as-a-service, infrastructure-as-a-service, support services, or similar online or offline managed, cloud services for the primary purposes of providing artificial intelligence platform infrastructure to third parties without explicit written permission. For clarity, artificial intelligence platform infrastructure encapsulates:

(i) data processing, transformation, and querying services for artificial intelligence, (ii) artificial intelligence training or artificial intelligence inference services, (iii) hosting and/or compute services for the above, and (iv) data center services and/or similarly situated services offering MAX in connection with a broader service offering to third parties.For the avoidance of doubt, this provision will not apply âex-post-factoâ (âafter the factâ) to any software you develop that is not competitive at the time you develop it, but later on becomes competitive to Modular based on Modularâs new product offerings or pivots resulting in an expanded definition of Competitive Activity. Development of any internal or personal use-only software is not a Competitive Activity. Please see our pricing page (https://www.modular.com/pricing) for more information and examples on Competitive Activities.

##### 3. REDISTRIBUTABLE COMPONENTS & LIMITATIONS

Modular hereby grants Licensee the limited right to redistribute certain components of the SDK as part of Licensee's Applications, subject to the conditions and limitations set forth in this clause, including the Distribution Requirements. The redistributable components of the SDK ("Redistributable Components") are specified in the accompanying SDK documentation or materials provided by Modular (the âDocumentationâ). Licensee shall only redistribute the Redistributable Components as expressly permitted in these Terms and in any Documentation, in accordance with the Distribution Requirements.

âDistribution Requirementsâ means each of the following requirements:

(a) The Application must have material additional functionality, beyond the included portions of the SDK;(b) The distributable portions of the SDK shall only be accessed by the Application;(c) The terms under which Licensee distributes an Application must be consistent with the terms of this Agreement, including (without limitation) terms relating to the license grant and license restrictions and protection of Modularâs intellectual property rights;(d) Licensee shall not redistribute the Redistributable Components as standalone components, libraries, or tools in any way that would enable third parties to violate the terms of this License;(e) Licensee shall not modify, reverse engineer, decompile, disassemble, or attempt to derive the source code of the Redistributable Components, except to the extent expressly permitted by applicable law;(f) Licensee shall include all proprietary notices, labels, and marks provided by Modular in the Documentation (or elsewhere) in connection with the Redistributable Components in all copies of Applications that incorporate the Redistributable Components;(g) The text of this Agreement shall be conspicuously displayed in each original or modified copy of the SDK; and(h) The Application must only be run on hardware expressly supported by MAX. If Licensee wishes to run the Application on custom hardware it is developing or has developed, Licensee must contact Modular with a written request to secure appropriate rights to do so. Modular reserves the right to approve or deny such requests in its sole discretion.

Licensee agrees to notify Modular in writing of any known or suspected violation of the Distribution Requirements and to reasonably enforce the terms of its Application. If Licensee receives the SDK in original or modified form from any third party, Licensee agrees and accepts that the terms of this Agreement are still binding upon Licensee.

##### 4. OWNERSHIP &Â PERSONALÂ INFORMATION

Modular and its applicable licensors retain all right, title, and interest in and to the SDK (including any Redistributable Components) and Documentation, including all intellectual property rights therein. Licensee acknowledges and agrees that, other than as explicitly stated herein, nothing in these Terms grants it any rights to or ownership interest in the SDK or Documentation. As between the parties, Licensee acknowledges that Modular owns all right, title, and interest in and to the Usage Data and may use or exploit it for any purpose it deems fit, including but not limited to the purposes of improving the SDK or any other component or aspect of the Max Platform. Any collection, storage, or use of Licensee personal information is governed by Modularâs Privacy Policy (https://www.modular.com/legal/privacy), which Licensee hereby acknowledges upon acceptance of these Terms.

##### 5. SUPPORT AND UPDATES

Modular is not obligated to provide any support, patches, fixes or updates for the SDK. However, Modular may, at its sole discretion, provide updates, patches, fixes or support at any time.

##### 6. FEES

The community license variant of the SDK is provided free of charge. Licensee acknowledges that Modular may at any time change its provision of its community variant of the SDK for free or generally. In the event Modular does the foregoing, it will make commercially reasonable efforts to provide Licensee of advanced notice of its plans to either change its provision of the SDK or offer the applicable, commercial variant of the SDK for a fee to the contact information provided by Licensee to Modular.

##### 7. WARRANTY DISCLAIMER

THE SDK IS PROVIDED "AS IS" AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, MODULAR MAKES NO OTHER WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. LICENSEE ACKNOWLEDGES AND AGREES THAT THE USE OF THE SDK IS AT THEIR OWN RISK, AND MODULAR DOES NOT MAKE ANY REPRESENTATIONS OR WARRANTIES REGARDING THE ACCURACY, RELIABILITY, OR COMPLETENESS OF THE SDK, OR INFORMATION OBTAINED THROUGH THE SDK. MODULAR DOES NOT WARRANT OR GUARANTEE THAT THE SDK WILL MEET LICENSEE'S REQUIREMENTS OR EXPECTATIONS FOR ITS APPLICATIONS OR GENERALLY, OR THAT ANY ERRORS OR DEFECTS IN THE SDK WILL BE CORRECTED. FURTHERMORE, MODULAR DOES NOT WARRANT OR MAKE ANY REPRESENTATIONS REGARDING THE RESULTS THAT MAY BE OBTAINED FROM THE USE OF THE SDK OR THE SUITABILITY OF THE SDK FOR ANY PARTICULAR PURPOSE. MODULAR MAKES NO WARRANTIES OR REPRESENTATIONS THAT THE SDK WILL BE COMPATIBLE WITH LICENSEE'S SYSTEMS, SOFTWARE, OR APPLICATIONS, OR THAT THE SDK WILL BE SECURE FROM UNAUTHORIZED ACCESS, HACKING, OR OTHER POTENTIAL SECURITY THREATS. MODULAR DISCLAIMS ANY LIABILITY ARISING OUT OF OR IN CONNECTION WITH THE USE OF THE SDK. LICENSEE ASSUMES ALL RISKS AND RESPONSIBILITIES FOR USING THE SDK TO ACHIEVE THEIR INTENDED RESULTS AND FOR THE INSTALLATION, USE, AND RESULTS OBTAINED FROM THE SDK. ADDITIONALLY, VERSIONS OF THE SDK IDENTIFIED AS âPREVIEW VERSIONSâ MAY HAVE ADDITIONAL BUGS OR DESIGN FLAWS. MODULAR MAKES NO WARRANTIES ABOUT THE PREVIEW VERSIONS WHATSOEVER, AND LICENSEEâS USE OF THE SDK IN COMMERCIAL, PRODUCTION ENVIRONMENTS IN VIOLATION OF THIS AGREEMENT IS AT ITS OWN RISK.

##### 8. INDEMNIFICATION

Licensee shall defend, indemnify and hold harmless Modular, its affiliates and their respective officers, directors, employees, agents and representatives from any and all third party claims, damages, liabilities, costs and fees (including reasonable attorneysâ fees) arising from: (i) its material breach of these Terms; or (ii) an allegation that any Application violates, misappropriates, or infringes any third party intellectual, contractual or proprietary right.

##### 9. LIMITATION OF LIABILITY

TO THE EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL MODULAR BE RESPONSIBLE OR LIABLE WITH RESPECT TO ANY SUBJECT MATTER OF THIS AGREEMENT UNDER ANY CONTRACT, NEGLIGENCE, STRICT LIABILITY OR OTHER THEORY FOR: (I) LOSS OR INACCURACY OF DATA OR COST OF PROCUREMENT OF SUBSTITUTE GOODS, SERVICES, OR TECHNOLOGY; OR (II) ANY INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES INCLUDING, BUT NOT LIMITED TO LOSS OF REVENUES OR PROFITS; (III) ANY MATTER BEYOND ITS REASONABLE CONTROL; OR (IV) ANY AMOUNT IN THE AGGREGATE OVER $100. SOME JURISDICTIONS DO NOT ALLOW CERTAIN WARRANTY DISCLAIMERS OR LIMITATIONS ON LIABILITY. ONLY DISCLAIMERS OR LIMITATIONS THAT ARE LAWFUL IN THE APPLICABLE JURISDICTION WILL APPLY TO YOU, AND MODULARâS LIABILITY WILL BE LIMITED TO THE MAXIMUM EXTENT PERMITTED BY LAW.

##### 10. TERMINATION

These Terms may be terminated if the Licensee wants to terminate this Agreement by stopping use of the SDK. Modular may terminate this License if the Licensee fails to comply with any term in this Agreement, or if the Licensee commences, or participates, in any legal proceed against Modular. Upon termination, Licensee shall immediately cease all use of the SDK. These terms may be also discontinued by Modular if it decides to no longer provide any SDK, or if in Modular soles discretion, continued use is no longer commercially viable. Modular will attempt to provide written notice of termination to Licensee either through a posting on its website, or via the contact method for Licensee it has available. Upon any termination of this License all provisions survive except for the license grant provisions.

##### 11. EXPORTÂ CONTROL

Licensee shall comply with, and shall, at Modularâs request, demonstrate compliance with all applicable export laws, restrictions, and regulations of any United States or foreign agency or authority. Licensee shall not export or re-export, or allow the export or re-export of any product, technology or information it obtains pursuant to these Terms (including but not limited to the SDK) in violation of any such laws, embargoes, restrictions or regulations. Licensee shall obtain and bear all expenses relating to any licenses and/or exemptions required to comply with foregoing.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog/the-next-big-step-in-mojo-open-source

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

March 28, 2024

# The Next Big Step in Mojoð¥ Open Source

Modular Team

At Modular, open source is ingrained in our DNA. We firmly believe for Mojo to reach its full potential, it must be open source. We have been progressively open-sourcing more of Mojo and parts of the MAX platform, and today weâre thrilled to announce the release of the core modules from the Mojo standard library under the Apache 2 license!

We have always believed that building Mojo in the open will lead to a better result, because it allows its design to be shaped by the feedback from the broader community. We released Mojo very early and have been driving steady improvements since May of 2023 (see the changelog). Building a language and its infrastructure is hard work and takes time, and weâre excited to move from sharing our work to collaborating with Mojo developers worldwide.

### Contribute to Mojoð¥ open source

There are many ways to open source code - some projects make source code available but do not accept contributions. Some provide opaque contribution processes without visibility into goals and roadmaps. Some open source and then donât actively maintain it.Â

At Modular, our team has either built, contributed to, or driven incredible open source projects like LLVM, Swift, TensorFlow and PyTorch, and we want to make sure we do things right. This means not just âmaking source code availableâ, but truly fostering and cultivating a vibrant and engaged open development community around Mojoð¥.

Beyond just providing source code, we are also opening up revision history for the standard library, releasing nightly builds of the Mojo compiler, providing public CI, and allowing external contributions through GitHub pull requests. This is expensive and non-trivial to set up, but in our experience, it's critically important to allow the community to scale.

### License: Apache 2 with LLVM exceptions

We chose to use Apache Licence, version 2.0. It contains a patent grant provision which provides legal protection to users and contributors of the software. The Apache 2 license is a widely used and proven license, and is familiar to many individuals and companies across the industry.

The Apache 2 license is a great start, but our experience with licensing in the LLVM project taught us that there are two small issues with it. Some people are concerned that the Apache 2 license may not mix well with GPL2 code (e.g. the Linux Kernel) and the Apache 2 license requires that you acknowledge use of the code in derived projects. We want you to be able to use Mojo without requiring you to acknowledge Modular or Mojo (though of course, you are welcome to do so!) and make it clear that it is fine to mix with GPL2 code. As such, weâre including the LLVM exceptions which were specifically designed to address these concerns.

We believe this represents the state of the art in open source licensing for language and tool projects, and recommend other Mojo open source projects to follow the same approach. We have been using this approach for our existing open source code, and will continue to release more code with the same approach.

### Early stages of our open source journey

The Mojo standard library is under heavy development and changing rapidly, so we've started by open sourcing its core modules. As such, we consider this to be an important starting point, not an end to our open source journey. Weâll similarly evolve and iterate our development and contribution processes as we learn together with the community. We'll be opening up a lot more code over time, including more of Mojo and parts of the broader MAX platform.

### Contribution guides

So how can you become a contributor to Mojo? There are a lot of areas in the standard library that can be improved, so make sure to check out the README.md in the Mojo GitHub repository on the nightly branch if you're planning to raise pull requests. It contains links to important docs, all of which are worth reading:

- Development Guide

- âContributing Guideâ

- âStyle Guide

- âVisionâ

- Governance Structureâ

- FAQs

We'll also be giving away swag and other benefits to Mojicians with significant contributions to the standard library!

## Nightly builds for fast iteration

Nightly builds of the Mojo compiler are also now available! This allows you to test your contributions to the standard library against the latest version of the Mojo compiler that corresponds to the library source. This is a massive step towards opening development of the Mojo compiler, and allows Mojicians to live on the bleeding edge of development if they choose and test against the current tree. Over time, we will expand this nightly build to include the entire MAX platform.

Make sure to check out the standard library development guide for details on how to get a nightly build, either to be on the bleeding edge of our development, or to test your contributions.

## Version control history

We're releasing not only the code, but also our commit history for the standard library. We are committed to transparency, and enabling you to see how features have evolved over time, which provides additional context on how to develop standard library features.

## Letâs build the future together!

We are very excited about the future of Mojo and all of the applications that people are already using it for. We think this is a big step forward in openness and transparency, though we still have a long way to go. If youâre interested in making contributions, please check out the README in mojo/stdlib for everything you need to get started, and feedback is always welcome on Discord and GitHub.

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Developer

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Modular Team

Company

Our mission is to have real, positive impact in the world by reinventing the way AI technology is developed and deployed into production with a next-generation developer platform.



================================================================================

URL: https://www.modular.com/enterprise#form

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# Accelerate AI innovation and scaleÂ globally.

Run AIÂ workloads more efficiently, and optimize your compute inside your enterprise.

Power all your AI use cases on one stack.

### Fastest GPU Infrastructure

Get out of the box performance for GenAI models on NVIDIA H100s and A100s.

### Unify your AI infrastructure stack

Unify industry frameworks and hardware, streamlining your deployment workflows to any cloud or on-prem environment.

### Deploy and scale for FREE with MAX

Package your pipelines once and deploy across CPUs and GPUs without having to change any code.

### Easiest way to optimize your existing models

Drop in your PyTorch or ONNX models and get an instant boost in performance with our next generation inference runtime.

- Deploy MAX inside your cloud environment

- Supercharge the efficiency of your AI stack with just 3 lines of code.

- Dedicated support from our world class AI infrastructure team.

Deploy MAX inside your cloud environment

Supercharge the efficiency of your AI stack with just 3 lines of code.

Dedicated support from our world class AI infrastructure team.

## Talk to our Sales Team

Tell us what tools your organization is using and we can work together to see how best to incorporate MAX.

## Get started now

- Getting started guideStep by step walkthrough on how to get started

- Browse examplesRun a project locally in just 2min

- Connect with buildersJoin over 22,000 developers

Getting started guide

Step by step walkthrough on how to get started

Browse examples

Run a project locally in just 2min

Connect with builders

Join over 22,000 developers

Download for your platformÂ now

- 

- 

- 

View Pricing

Contact Sales

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

MAX on GPU waitingÂ list

Be the first to get lightning fast inference speed on your GPUs. Be the envy of all your competitors and lower your compute spend.

================================================================================

URL: https://www.modular.com/blog/announcing-stack-pr-an-open-source-tool-for-managing-stacked-prs-on-github

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

July 23, 2024

# Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

Mikhail Zolotukhin

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

### What are stacked PRs?

Stacked PRs are a way to structure pull requests in a way when one PR depends on another in a sequence. Instead of submitting one large PR, you can break your changes into smaller, more manageable PRs that build on each other. As each PR in the stack is reviewed and merged, the remaining PRs are automatically updated, ensuring a smooth integration process.

### What are the benefits of using stacked PRs?

Stacked PRs offer a structured way to manage multiple interdependent changes in a codebase. By breaking down large changes into smaller, more focused PRs, developers can benefit from:

- Better code reviews: Each PR can be reviewed independently, making it easier to identify issues and understand changes.

- Parallel work: You can proceed with work on a next change before another required change is fully reviewed and merged.

- Cleaner history: The commit history remains more organized, reflecting the logical progression of changes.

In the diagram below, the first graph demonstrates a typical workflows without stacked PRs. In the second graph, you can see the updated and simplified workflow with stacked PRs:

### What is stack-pr and how to use it?

stack-pr is a CLI tool for managing stacked PRs from the comfort of your terminal. With a simple interface it allows you to create, update and merge stacked PRs.

stack-pr offers a simple mental model for managing stacks of PR - each commit in your local git branch becomes a separate PR dependent on the PR from the previous commit. When you want to update the stack - e.g. either include some changes to existing PRs, reorder them, drop some of them, or add new PRs - you simply make the corresponding changes in your local git branch (usually youâll be using git rebase -i for this) and then run a single command stack-pr export to update the PRs on GitHub correspondingly. When you are ready to merge your PRs, you can use the stack-pr land command, which would land the bottommost PR and rebase the rest of the stack.

You can find a more detailed manual on how to use the tool in README.md.

README.md

### Acknowledging similar tools

stack-pr is not the first tool in the space, and weâd like to acknowledge other existing tools - specifically ghstack which heavily inspired stack-pr. ghstack is also a great tool for working with PR stacks, however it has some limitations which we worked around in stack-pr. For example, ghstack requires you to allow force pushes to your repo, and also it generates 3x more branches when constructing a stack compared to our tool. Nevertheless, we highly recommend giving it a shot too!

### Why are we open-sourcing stack-pr?

Our company believes in giving back to the community. By open-sourcing this tool, we hope to contribute to the ecosystem and encourage collaboration and improvement. We invite developers, teams, and open-source enthusiasts to try out our tool, provide feedback, and contribute to its development. stack-pr is in its early days and there are many things that can be improved, and while you can use it as-is right now, we also welcome contributions to the tool to make it better for everyone!

Thank you for your support, and we look forward to seeing how this tool evolves with your contributions! You can get stack-pr on GitHub, and be sure to check out the rest of the Modular community, which includes an active Discord where you can share feedback and connect with other devs.

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Engineering

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Mikhail Zolotukhin

AI Compiler Engineer

Expert in traditional and ML compilers with over 15 years of experience in the field. Before Modular, Michael contributed to Intel Compiler, GCC, LLVM, and most recently PyTorch. He is passionate about building right things the right way and excited to do that at Modular to help power AI for the world.



================================================================================

URL: https://www.modular.com/blog-all

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?topic=Product

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/team/modular-team

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?author=Modular+Team

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog/deep-dive-into-ownership-in-mojo

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

June 4, 2024

# Deep dive into ownership in Mojo

Ehsan M. Kermani

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

Understanding how ownership works in Mojo is essential to leveraging its memory management capabilities effectively. Mojo ensures memory safety similar to Rust and efficiency of C/C++ by enforcing strict ownership rules. In this part, we will explore how this is achieved through different kinds of values, ownership modifiers, and lifecycle management.Â

### Value kinds in Mojo

When Mojo compiler parses the code along with the type checker it outputs three kinds of values, each with distinct ownership and reference behavior:Â

- RValue (Owned Value): Represents a unique ownership, i.e., a unique reference or a unique typed pointer with a lifetime.Â

- LValue (Mutable Reference): Represents a mutable reference, allowing read and write access to the original value.Â

- BValue (Immutable Reference): Represents an immutable reference, allowing read-only access to the original value.

Intuitively, these values can be defined using the ownership model we built in the previous section as follows (Mojo-pseudocode):

These values propagate through the expression trees as the result of the Mojo parse. This is crucial for ensuring that the correct ownership and reference semantics are maintained throughout the program.Â

Next, we will see how ownership can be modified in function arguments.

### Ownership Modifiers in Function Arguments

Mojoâs argument convention in functions are as follows:

- borrowed: The function receives an immutable reference (similar to ImmutableRef discussed previously). This means the function can read the original value (it is not a copy), but it cannot mutate (modify) it. Note that this is default in fn foo(x: T) i.e. fn foo(borrowed x: T)

- inout: The function receives a mutable reference (similar to MutableRef). This means the function can read and mutate the original value (it is not a copy).Â

- owned: This means the function has an exclusive unique referenceÂ  (similar to UniqueRef) which can uniquely identify the underlying value. Often, this also implies that the caller should transfer ownership to this function, but that's not always what happens and this might instead be a copy. More on this later.

The first rule to note is:

Rule 1: owned arguments take RValue on the caller side but are LValue on the callee side.

When a function argument is declared as owned, it takes an RValue on the caller side but becomes an LValue on the callee side. This ensures that the function has exclusive access to the value, preventing other references from accessing or modifying it simultaneously. (The following is valid Mojo code):

In above, String("hello, world!") uses the constructor __init__ to create the RValue where var owns it and is named x. Then in take_ownership(x^), the x^ is passed as an RValue (caller side) whereas it is received as an LValue in the callee side which is x = String(âBye!â). The output of the program is Bye! as expected.

For the purpose of completeness, we should mention that Mojoâs dataflow analysis determines where the last use of a variable has occurred and it injects its destructor __del__ eagerly. This is known as As Soon As Possible (ASAP) destructor. However, when transfer operator ^ is used, Mojo disables the call to String.__del__ and is correctly delegated to happen after take_ownership is done printing the new assigned value. For more, please check out the death of a value.Â

#### Relations of the value kinds and ownership modifiers

This table depicts how Value kinds behave with function ownership modifiers.

### Establish ownership behavior through __copyinit__ and __moveinit__

To implement custom behavior for copying and moving values, Mojo provides the __copyinit__ and __moveinit__ dunder methods, respectively. These methods allow you to define how values are copied or moved, ensuring that ownership is correctly transferred without unintended side effects. For more details, please check out the Mojo manual.

A type in Mojo can be:

#### Movable

Values that can be moved through __moveinit__, transferring ownership without copying the underlying data through the transfer operator ^. The transfer operator ^ is used to convert a BValue or LValue into an RValue. This is essential for transferring ownership while maintaining the original valueâs integrity.

The following code simply prints when __moveinit__ is called:

In the above example, var b = a fails to compile because that assignment used the __copyinit__ which there is none here. Executing the code, outputs Move is called.

#### Copyable

Values that can be copied through __copyinit__. The following code simply prints when __copyinit__ is called:

The above var b = a calls the __copyinit__ and running the code confirms Copy is called. Note that we need to use keep(a) to keep the Mojo compiler away from optimizing it away as it was unused.

An important note is that the transfer operator ^ is still usable here and its behavior is delegated to __copyinit__. We can verify it as follows:

which outputs:

This might be surprising at first! In order for transfer operator to actually move, we need to implement  __moveinit__.

#### Copyable and Movable

Values can implement both __copyinit__ and __moveinit__. The following prints when __copyinit__ or __moveinit__ is called.

which outputs:

Given the above examples, we can deduce the following:

Rule 2: owned argument owns the type if the transfer operator ^ is used, otherwise it copies if the type is Copyable.

The Mojo compiler takes any opportunity to optimize memory management. In the above example, even without specifying var c = a^

The Mojo compiler sees that var c = a is the last use of a so it optimizes move over copy.

Therefore, we can deduce the last rule:

Rule 3: Copy to move optimization if the type is Copyable and Movable on last use.

This is particularly beneficial for large or complex data structures, reducing the overhead associated with copying and enhancing performance. For example, in the following, avoiding extra copy is an important optimization that the Mojo compiler can do easily for us.

which the expected output is:

#### Immovable

Values that cannot be copied or moved, ensuring that they remain in a fixed memory location. An example of such special immovable types is Atomic which cannot be copied or moved. Such behavior is critical to ensure correctness in multithreaded environments.

### Conclusion

In the second part of the ownership series in Mojo, we built on the mental model developed in the first partÂ and provided practical examples to illustrate how ownership works in Mojo. We covered the different kinds of values (BValue, LValue, and RValue) and how they propagate through expressions. We also explained the function argument conventions (borrowed, inout, owned) and demonstrated how these conventions help manage memory safely and efficiently. We concluded with three fundamental rules:Â

- Rule 1: Owned arguments take RValue on the caller side but are LValue on the callee side.

- Rule 2: Owned arguments own the type if the transfer operator ^ is used; otherwise, they copy the type if it is Copyable.Â

- Rule 3: Copy operations are optimized to move operations if the type is Copyable and Movable and isnât used anymore, reducing unnecessary overhead.Â

Lastly, we emphasized that the main goals of ownership in Mojo are:

- Memory Safety: Enforcing exclusive ownership and proper lifetimes to prevent memory errors such as use-after-free and double-free.Â

- Performance Optimization: Converting unnecessary copy operations into move operations to reduce overhead and enhance performance.Â

- Ease of Use: Automating memory management through ownership rules and the transfer operator, simplifying development.Â

- Compile-Time Guarantees: Providing strong compile-time guarantees through type-checking and dataflow lifetime analysis, catching errors early in the development process.Â

By integrating these principles, Mojo offers a powerful and intuitive framework for memory management, enabling developers to write efficient, safe, and high-performance code. For a deeper understanding of the technical details and implementation of ownership in Mojo, make sure to watch the accompanying video by our CEO, Chris Lattner.

Additional resources:

- Get started with Mojo

- Official Mojo manualâ

- Join our Discord communityÂ

- Contribute to discussions on the Mojo and MAX GitHub

Report feedback, including issues on our Mojo and MAX GitHub tracker.

Until next time!ð¥

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Developer

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Ehsan M. Kermani

AI DevRel

Ehsan is a Seasoned Machine Learning Engineer with a decade of experience and a rich background in Mathematics and Computer Science. His expertise lies in the development of cutting-edge Machine Learning and Deep Learning systems ranging from Natural Language Processing, Computer Vision, Generative AI and LLMs, Time Series Forecasting and Anomaly Detection while ensuring proper MLOps practices are in-place. Beyond his technical skills, he is very passionate about demystifying complex concepts by creating high-quality and engaging content. His goal is to empower and inspire the developer community through clear, accessible communication and innovative problem-solving. Ehsan lives in Vancouver, Canada.



================================================================================

URL: https://www.modular.com/blog/whats-new-in-mojo-24-4-improved-collections-new-traits-os-module-features-and-core-language-enhancements

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

June 17, 2024

# Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Shashank Prasanna

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

Throughout the rest of the blog post, weâll discuss many of the new features in this release with code examples that you can find in a Jupyter Notebook on GitHub. As always, the official changelog has an exhaustive list of new features, whatâs changed, whatâs removed, and whatâs fixed. Before we continue, donât forget to upgrade your Mojoð¥. Letâs dive into the new features.

### Improved Collections: New List and Dict features

In Mojo 24.4 List and Dict introduce several new features to make them even more Pythonic. Many of these features have come directly from our community:

- List has new index(), count(), __contains__() and conforms to Stringable trait, thanks to contributions by @gabrieldemarmiesse, and @rd4comÂ

- Dict has new clear(), reversed(), get(key), items(), values() and conforms to Stringable trait, thanks to contributions by @jayzhan211, @gabrieldemarmiesse, and @martinvuyk

Letâs take a look at how to use these through code examples.

#### Enhancements to List

In this example, weâll calculate word frequency from the contents of a webpage and plot the results as a word cloud. In the code below, we use Mojo's Python interoperability feature to preprocess the text, tokenize it, and remove stop words. We also use the Python interoperability features to plot the results. For the purpose of demonstration, weâll use the url = "https://docs.modular.com/mojo/manual/basics" to generate the word cloud from the Mojo manual. Feel free to experiment with different URLs. Now, letâs take a look at the code.

Output:

In the example above, first we fetch a list of filtered words from the webpage in url using utils.fetch_and_preprocess_text(url). After that weâre ready to calculate word frequencies.Â

We highlight the use of __contains__() in the code word[] not in unique_words: where the condition checks whether the current word is already in the unique_words list. If the word is not in unique_words, it means this is the first time we are encountering this word. If the word is not already in unique_words, we append it to the unique_words list. We then count how many times this word appears in the original words list using the count() method. mojo_word_list.count(word) counts the number of occurrences of word[] in mojo_word_list. The frequency count of the word is appended to the word_frequencies list at the same index as the word in unique_words. This means that unique_words and word_frequencies will have corresponding elements.Â

To plot the word cloud, each word is plotted at a random position with a size proportional to its frequency, and random colors are used for the words. We use the Python interoperability features to call the plotting function in utils.plot_word_cloud().Â

#### Enhancements to Dict

In this example weâll use the Monte Carlo method for approximating the approximate value of Pi using new Dict features. The Monte Carlo method approximates pi by randomly generating points within a unit square and counting the number of points that fall within the unit circle inscribed within the square. The ratio of the points inside the circle to the total points can be used to approximate. Weâve written about this in more detail in an earlier blog post. Be sure to read that for more details on the math behind why this works. In this demo below we use Dict to methods exclusively instead of Arrays to implement the solution.

Output:

In the example above, we first create a Points dictionary, from new fromkeys static method to initialize Dict with keys from the List variable keys and set values to be empty lists. We use points_data.items() to print each itemsâ key and value. We also use reversed() to print the reversed order of keys. We also use get(key) to retrieve the points data for a specific key ("inside" in this case) and since the output of get() is an Optional type we use the take() function to retrieve the value. Finally we also demonstrate the use of clear to clear the dictionary. To plot the image above we made use of a utility function in Python using Python interoperability, called utils.plot_points()

### New traits: Absable, Powable, Representable, Indexer

Mojo 24.4 also includes new traits to make writing Math equations simpler, print string representation of objects and define containers that can be indexed using integral values.

- Structs that conform to Absable and Powable traits will work with built in abs() and pow() functions. Absable types must implement __abs__() dunder method and Powable type must implement the __pow__() dunder method. Powable types can also be used with operator ** in addition to the pow() function.

- Objects of structs that conform to the Representable trait must implement a __repr__() dunder method, which enables the repr() to be called on objects to provide a string that can, if possible, be used to recreate the object and can be very useful for debugging. Thanks to @gabrieldemarmiesse for contributing this feature.

- Structs that conform to the Indexer trait allow their objects to be used as index variables that can be passed to __getitem__() and __setitem__(). Types conforming to the Indexer trait implement __index__() dunder method and are implicitly convertible to Int or by calling the built in function index().

In the example below Iâve implemented a struct called MojoArray that conforms to Absable, Powable, and Representable traits, therefore it implements __abs__(), __pow__() and __repr__() dunder methods. Here is the skeleton of our struct, the full implementation is available on GitHub.

In the code example above, we compute vectorized __abs__() and __pow__() as follows:

And __repr__() as follows:

Now, letâs create an object of the MojoArray struct to see these methods in action.

Output:

As you can see writing abs(v1-v2)**exp is a very expressive way to write math equations with these new traits vs. something like (v1-v2).abs().pow(exp) which is what weâd have done previously.

Mojo 24.4 also includes a few other math specific traits math.Ceilable, math.CeilDivable, math.CeilDivableRaising, math.Floorable, and Truncable. See the changelog for more details.

### os module enhancements

Mojo 24.4 also includes several file IO enhancements that makes Mojo standard libraryâs os module more Pythonic. Particularly, this release introduces the following functions: mkdir(), rmdir(), os.path.getsize(), os.path.join() and a new tempfile module that implements gettempdir() and mkdtemp() functions, thanks to contributions from @artemiogr97. Letâs take a look at an example to see how to use these methods in your own projects.

Output:

### base64 package enhancements

This release also includes a new base64 package that offers encoding and decoding support for both the Base64 and Base16 encoding schemes. Base64 encoding is often used in tokenizers for large language models (LLMs) and we use it in our implementations of bpe and tiktoken tokenizer utilities for Llama3. Check out our Llama3 example in this repository. Letâs take a look at an example that show you how to use this new package in the Mojo standard library.

Output:

You can see that the original and decoded images plotted side by side are the same. We use the utils.plot_original_decoded_images() helper function written in Python and called from Mojo using the Python interoperability feature.

### Core language features

Mojo 24.4 also includes several core language features that are a bit harder to demonstrate with code examples. Most notably, this release Mojo has updated how def function arguments are handled. Previously, arguments were copied by default (owned convention), making them mutable but potentially causing performance issues due to unnecessary copies. Now, arguments use borrowed convention by default and only copied if mutated within the function.Â

In this release you can also return multiple values from a function as a Tuple that can be unpacked into individual variables. For example in the earlier Enhancements to Dict section we define a method: def approximate_pi(num_points: Int) -> (Float64, Dict[String, List[List[Float64]]]). We call this method in this way to get the values of the Tuple into separate variables: pi_approximation, points_data = approximate_pi(num_points), where as previously we had to get the value as a Tuple variable and index into it to extract the values.

This release also introduces the new @parameter loop decorator which can be used with for loops where the loop variable is a compile time constant. This allows the Mojo compiler to perform a full unroll of the loop to improve performance. With the introduction of @parameter decorator for loops, the previously recommended @unroll decorator has now been deprecated.

There are many more core language enhancements in this release, see the changelog for a complete list.

### New documentation pages

We also updated our documentation to include dedicated pages that dive deeper into the following topics:

- Control flow: https://docs.modular.com/mojo/manual/control-flow

- Testing: https://docs.modular.com/mojo/tools/testingÂ

- Unsafe pointers: https://docs.modular.com/mojo/manual/pointersÂ

### But wait, thereâs more!

Mojo 24.4 includes many more features that we didnât cover in this blog post. Check out the changelog for a detailed list of whatâs new, changed, moved, renamed, and fixed in this release.Â

MAX 24.4 is also available for download today and for the first time weâre making it available on Mac OS. This release of MAX also includes several enhancements including New Quantization API for MAX Graphs, and full implementation of Llama 2 and Llama 3 models using Graph API with quantization. Read more in the MAX 24.4 announcement blog post.

All the examples I used in this blog post are available in a Jupyter Notebook on GitHub, check it out!

- Download MAX and Mojo.

- Head over to the docs to read the Mojoð¥ manual and learn about APIs.

- Explore the examples on GitHub.

- Join our Discord community.

- Contribute toÂ  discussions on the Mojo GitHub.

- Read and subscribe to Modverse Newsletter.

- Read Mojo blog posts, watch developer videos and past live streams.

- Report feedback, including issues on our GitHub tracker.

Until next time! ð¥

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Developer

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Shashank Prasanna

AI Developer Advocate

Shashank is an engineer, educator and doodler. He writes and talks about machine learning, specialized machine learning hardware (AI Accelerators) and AI Infrastructure in the cloud. He previously worked at Meta, AWS, NVIDIA, MathWorks (MATLAB) and Oracle in developer relations and marketing, product management, and software development roles and hold an M.S. in electrical engineering.



================================================================================

URL: https://www.modular.com/blog/debugging-in-mojo

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

July 16, 2024

# Debugging in Mojoð¥

Jack Clayton

Walter Erquinigo

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

This tutorial is for VS Code, but can be adapted to other editors using LLDB-DAP. It's aimed at general debugging if you have no experience with it, but goes over all the currently implemented Mojo features. The goal is to show you how simple and powerful debugging is with Mojo, to improve your velocity tracking down logic errors beyond writing and deleting print statements.

### Debug current file

First make sure you've installed the Mojo extension, or Mojo Nightly if you've installed the nightly compiler.

Let's start with the simplest way to boot up the debugger in VS Code, set a breakpoint by clicking to the left of a line to set a ð´ Breakpoint, then press the â to the right of the â¶ï¸ Play button in the top-right, and choose Debug Mojo File:

Note:Â you can expand any GIF by clicking it, and minimize by clicking anywhere or pressing any key.

Pressing the |â¶ Continue button advances to the next break point, if the same line of code is being hit in a loop, this will advance to the next iteration in the loop.

Default Keybindings:

- F5: Start debugger (select Mojo on the first run if starting this way)

- F5: Continue to next breakpoint if the debugger has started

- Shift+F5: Stop the debug session

- Control+Shift+P (Command+Shift+P on macOS): Restart the debug session

- âF9: Toggle breakpoint on the current line

Control+Shift+P

Command+Shift+P

You can also launch the debugger from a terminal with:

Type in mojo debug --help for more options.

### Step in, out, and over

A powerful feature is stepping into functions, out of them, and over any breakpoints you've set:

The first action is a Step Into the List.append method, followed by 2x Step Over to break at the following expression, and finally a Step Out to return to the original List.append breakpoint.

Default Shortcuts:

- F10: Step Over.

- F11: Step Into.

- Shift+F11: Step Out.

### Mutating and inspecting variables

You can mutate variables mid-debug session! Simply click on a value to edit it. You can also use the debug console to index into variables:

### Changing stack frame context

When you step into a function, the stack frame and local variables will change on the debug panel, you can view a different stack frame by clicking on it:

You'll notice the local variables in the debug panel change, you can inspect and modify them as usual.

### Watches

Sometimes the local variable you're interested in won't be in the Locals section, or perhaps there are too many variables there, and you want to keep a specific variable at the top of the list, or you might want to use an expression such as indexing into a list. To achieve this you can press the â button next to WATCH:

### Logpoints

Instead of writing print statements into your program, and potentially committing them to your PR, you can use a log point expression which also has code completions via the Mojo LSP. Simple right click where you would normally set a breakpoint and choose Add Logpoint:

### Hit Count

If you want to wait for x iterations until your breakpoint triggers, you can use a Hit Count. Right click a breakpoint location and select Conditional Breakpoint then change the selection to Hit Count, in the example I'm choosing the third iteration. Breakpoints are triggered just before the current line expression runs, which in this example is just before the third element is added to the List:

### Wait for breakpoint

If you want to skip a breakpoint until another one has been triggered, you can use this. Right click a breakpoint location and select Triggered Breakpoint, then choose the other breakpoint you want to wait for:

### Multithreaded debugging

You can also debug in a multithreaded context. If you go to the command input of the DEBUG CONSOLE, type in :thread list to see a list of the current threads, any threads that are currently paused due to breakpoints will display here. You can then change the stack frame into those threads to inspect local variables:

### Custom Launch Configuration

If you need more control over your debug launch configuration for a larger program, or you're running server and client and you need to attach to a process, you can create a launch.json file. You can also commit this to the repo so other users can easily start a debug session. First Press the Debug icon on the sidebar, then click create a launch.json file and select Mojo to create a file at .vscode/launch.json:

Press Control + Space to discover all the configuration options. Make sure to change [your-program.mojo] and [your-binary] to your desired targets, and delete any configuration you don't need.

Now on the top right of the debug panel, you can select a launch configuration which will run when you press F5:

### Coming Soon

- Break on error, for easily tracking down what part of your code is throwing.

- Conditional breakpoints, for expressions to break on True.

- GPU Debugging! See a preview below:

For more details on Mojo debugging check out the debugging docs.

For more details on VSÂ Code debugging, check out their docs here.

â

Thank you for reading! Here are some additional Mojo resources:Â

- Get started with downloading Mojo

- Head over to the docs to read the programming manual and learn about APIs

- Explore the examples on GitHub

- Join our Discord community

- Contribute toÂ  discussions on the Mojo GitHub

- Read and subscribe to Modverse NewsletterÂ

- Read Mojo blog posts, watch developer videos and past live streams

- Report feedback, including issues on our GitHub tracker

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Engineering

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Jack Clayton

AI Developer Advocate

Jack started his career optimizing autonomous truck software for leading mining companies, including BHP and Caterpillar. Most recently he was designing computer vision software, putting AI inference pipelines into production for IDVerse. He is enormously passionate about the developer community, having been a Rust, Go, Python and C++ developer for over a decade. Jack enjoys making complicated topics simple and fun to learn, and heâs dedicated to teaching the world about Mojo ð¥.



Walter Erquinigo

AI Tools Engineer

Walter has over 10 years of experience focusing on becoming a well rounded engineer. He has experience all over the stack, from very low level CPU tracing to front end development. However, he is particularly passionate about developer productivity and debuggers, which is an area where he pushed the state of the art by working reverse debugging at Meta. When not working, he likes to play the piano, travel all over the world and spend time with his friends and family.



================================================================================

URL: https://www.modular.com/blog/a-brief-guide-to-the-mojo-n-body-example

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

July 3, 2024

# A brief guide to the Mojo n-body example

Chris Hoge

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

N-body is one of the Computer Language Benchmark Game's benchmarks and is described as a program that âmodels the orbits of Jovian planets, using a simple symplectic integrator.â This benchmark does an excellent job of exercising single-core numeric performance. Because itâs a simple ordinary differential equation (ODE) solver, itâs not easily parallelizable (although, as we will see, it is possible to implement basic vectorization). In addition to this, numerically integrating orbital dynamics adds a level of complexity, as itâs easy for small integration errors in an implementation to grow exponentially in time, destabilizing the computation. This makes the n-body problem a good test both the speed and accuracy of an implementation.

This post will give a brief guide to the Mojo n-body example, describing the code in detail. While it doesn't go too deep into math, it will teach a little bit about orbital mechanics. Along the way, it will show how you can use Mojoâs built-in features and standard library to start writing your own optimized code.

### Installing Mojo

Begin by installing Mojo, which is available as part of the MAX framework. Complete instructions on installing MAX and Mojo are in the MAX documentation. Once Mojo is installed, you can get the n-body example code from the Mojo GitHub repository.

The example code can be found in the mojo/examples directory.

### Importing from the Mojo standard library

The example begins by importing several modules from the standard library.

The standard library includes many of Mojoâs features, including common math functions, benchmarking tools, standard collection types such as lists, and testing and validation methods.

### Setting up the data structures

Next, we define the constants that will be used throughout the simulation.

Although the standard library math module includes pi (math.pi) as a constant, it is specified here for convenience and consistency with other simulations from the Benchmarks Game.

One theme that will frequently arise in the explanation of this simulation is how choices are made to speed up, simplify, and stabilize the computation. The selection of units of measurement is an excellent example of this. In this simulation, the unit of mass is chosen as the mass of the Sun divided by 4Ï2, the unit of time is one year, and the unit of distance is one Astronomical Unit (AU), roughly equivalent to the average distance from the Earth to the Sun.

Why choose these units?

Weâre considering an orbital dynamic system that follows classical mechanics for this simulation. This means that we can use Newtonâs Law of Universal Gravitation to model the bodies' interactions.

It follows from this assumption that Keplerâs Laws of Planetary Motion will hold, which we can use to guide our choice of units.

Keplerâs Third Law states that T2/a3 is constant, where T is defined as the orbital period of a small body orbiting a larger body, and a is the distance between the smaller and larger body. For the Earth and the Sun, this constant is approximately GM/4Ï2, where G is the gravitational constant, and M is the mass of the Sun.

Using this knowledge, determined by experimental observation, we choose our units of mass to be 4Ï2 multiplied by the mass of the Sun. That means that in this simulation with this choice of units, the gravitational constant is 1. You can dig into this more deeply in the Wikipedia page for Keplerâs Laws of Planetary Motion.

Not only do these choices simplify computing gravitational acceleration by removing a multiplication, they also helps to scale our computations into a reasonable range, reducing the likelihood of introducing numerical instability into the simulation because of rounding errors. The choice of units effectively normalizes the equations of motion.

With the units set, itâs time to define the data structure for planets. Each planet (and the Sun) has three major components that define its state:

- A variable position in 3D space.

- A variable velocity in 3D space.

- A constant mass.

The planet structure is represented by a Mojo struct, modified by the @value decorator, which tells the Mojo compiler to synthesize essential lifecycle methods to give the structure full value semantics. This means it will automatically create __init__(), __copyinit__(), and __moveinit__() methods that are compatible with Mojoâs ownership model. When dealing with simple structures that are collections of built-in types, this simplifies your code base, making it easier to read and more reliable.

The position and velocity vectors are internally represented as Mojo SIMD types containing float64 vectors of length 4. Using a vector of length 4 may come as a surprise to youbecause position and velocity only have three dimensions.

SIMD data types require vector widths that will fit into computational registers, limiting the choice of vector length. A SIMD vector length of 4 is the smallest size that can hold the position and velocities of the planets, so we need to âwasteâ one float64 value in exchange for parallelized computations. This is an example of a tradeoff between space: using 4 floats to hold 3 values, and time: being able to use SIMD operations to simultaneously perform one computation across three values.

One key to this working properly is to ensure that when the planet objects are initialized, the last item in each SIMD type is set to zero to prevent these extra values from corrupting the computed solution. This technique, known as padding, is common in writing optimized computational kernels: it takes advantage of vectorized operations while computationally âignoringâ the padded values weâre not interested in.

With our Planet data structure in place, we can now create representations of the planets with initial states that include position and velocity.

The entire initial state is stored as a list of celestial bodies, with the first entry being the largest object, the Sun. Position is encoded as AUs, velocity as AUs/year, and mass is scaled relative to the Sunâs solar mass divided by 4Ï2. The initial state is set with observational data converted to the appropriate units. How the values for the initial states were determined is beyond the scope of this blog post, but they were drawn from the Benchmarks Game simulation.

In addition to the Sun, this simulation only concerns four Jovian bodies in the outer solar system: Jupiter, Saturn, Uranus, and Neptune. The Sun is defined as at the simulation's center, with position and velocity of zero. Notice in the initialization of the Sun, we can initialize all SIMD array elements by passing a single value to the constructor. For the SIMD data type, Mojo assumes that you want to initialize every item in the vector to the same value if you only pass the constructor a single value.

For the planets, we explicitly construct them using four values: three non-zero values for each of the positions and velocities and a zero in the fourth dimension that prevents the final value from corrupting the SIMD reduce operations that weâll encounter later in the code.

The simulation's initial and ongoing state is stored as a List, one of the collection modules available in Mojoâs standard library. Recall that by decorating the Planet struct with @value, we immediately gained full compatibility with Mojoâs `List` collection type.

The initialization block also features another Mojo optimization feature: alias. Aliases define a compile-time temporary value that can't change at runtime. We can use aliases to fix the initial state at compile time and then use runtime variables (var) for the computation.

### Computing the momentum offset

Before running the simulation, we have to attend to one more bit of initialization. Although the Sun was set as the fixed center of the simulation, the velocity of the center of mass of the entire simulation is currently non-zero. This means that as we compute the equations of motion, the center of mass in the simulation will move at a constant speed through space. This additional motion can lead to less numerical stability, and we can counteract that by ensuring that the center of mass of the simulation is stationary.

To do this, we compute a momentum offset based on the initial state and then adjust the Sun's position and velocity to account for that offset. This will help prevent the simulation from drifting over time, maintain a stable frame of reference, and ensure realistic and accurate results.

Momentum is defined as a body's mass times its velocity. In the offset momentum method, we iterate over every body in our simulation, summing each body's momentum to compute the total simulation momentum. We then subtract that momentum from the Sun by adjusting its initial velocity (previously set to zero).

Note that this method takes the list of planets as inout parameter, making the list a mutable reference. This means that once the method returns, the caller will see all of the modifications made to the list of planets in the method.

### Integrating the dynamic system

The majority of the work in the simulation is done in the advance function, which integrates the equations of motion forward by one time step. It takes the list of planets as an inout mutable reference parameter and a time step parameter, dt, which by default is an immutable reference following Mojoâs borrowed argument conventions.

At a high level, it computes the change in velocity imposed upon each body by the gravitational force each body exerts on the other. Then, using the updated velocity, it updates the position of each body. This two step update is possible because the algorithm uses a symplectic integrator, derived from the equations of motion, to determine the updates for each body in the system. This is also known as the semi-implicit Eulerâs method. While the complete derivation of this method is outside the scope of this blog post, we can note that symplectic integrators are useful for planetary dynamics systems because they conserve energy. In contrast, general ordinary differential equation solvers donât and can introduce instabilities into the simulation.

There are a few things to note about this implementation. First, by necessity, this is an O(n2) algorithm in the number of bodies. Each planetary body has a gravitational influence on the other, requiring on the order of n2 computations. However, because the force exerted between bodies is symmetrical, we can cut the number of computations in half by computing the force between two objects only once.

Second, we can use SIMD operations to compute additions and multiplications more quickly because each dimension is independent. We also use the SIMD reduce_add operation to help calculate the squared distance between bodies. This is why it was essential to pad the fourth position and velocity values with zeroes to ensure that undefined memory initialization doesn't impact the computation.

Third, because we chose our units of measurement such that the gravitational constant is 1, it doesnât appear in the computation.

### Computing the energy of the system

We compute the system's total energy by summing each body's kinetic energy with the potential energy between each body. We can use this computation to check the correctness and stability of our simulation. Because itâs a closed system, the total energy should remain constant, and the symplectic integrator was chosen to maintain this invariant.

The kinetic energy is simply Â½mv2 (with SIMD multiplication and reduce_add coming into play again), with m being the mass of the body and v the velocity. The potential energy between two bodies is -GMm/r, where G is the gravitational constant, M is the mass of the first body, m is the mass of the second body, and r is the distance between them. A quick reminder is that because of our initial choice of units, G in this equation 1, and the potential energy is just the product of the masses divided by the distance between them.

### Putting it all together

We have all the pieces we need to run the simulation. We begin by initializing the simulation and creating a copy of the system's initial state. We also compute the system's energy at the start of the simulation to verify its stability at the end.

We run the simulation for 50 million steps, with a time step of 0.01. This means the simulation projects ahead 3.5 days at a time to predict 50 thousand years of orbital dynamics. With 50 million calls to the advance function, we can easily see the savings in guaranteeing that `advance` is treated as inline code rather than a function call.

At the end of the simulation, we recompute the system energy and verify that itâs equal to the original energy -- within computational accuracy. Note that to help with readability, we can use underscores to punctuate large numbers.

### Benchmarking the code

We can provide an alternative method to benchmark the simulation's performance instead of just running it. To guarantee the accuracy of the benchmarking code, we use the keep function from the standard library benchmark package to ensure that the Mojo compiler doesnât optimize the energy calculations. Usually, if Mojo determines that a method has no side effects, it will optimize that code away.

## Running the simulation

To build the simulation as a standalone program, we need to provide a main function as an entrypoint for the application.

You can build the application using the command:

To run the benchmarked version of the code, change the run_system command to benchmark, recompile and run. On an Intel x86 system with 512 bit wide SIMD, benchmarking 50 million iterations three times yields:

When compared to the C implementation that is very similar in structure, compiled with optimizations for the same architecture:

The Mojo implementation is approximately 46% faster. While the implementations are similar, Mojo get a significant performance boost right out of the box because ofit's build-in support for SIMD operations. This is just the beginning of possible optimizations, and there's a lot more that can be done to improve the performance of this example.

## Whatâs next?

This blog post briefly introduced Mojo through the nbody.mojo example, and explained how its built-in data types can be used to write performant code with minimum effort.

While this code is fast out of the box, there is still room for improvement. What optimizations can you write to make the code even faster? Or if you want to take on a new challenge, you can try implementing one of the many other benchmarks in Mojo.

Share your implementations and feedback with the Modular Discord community, and contribute your improvements to nbody.mojo at the Mojo repository on GitHub.

Don't forget to check out the entire collection of community resources on the Modular community page!

Until next time! ð¥

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Developer

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Chris Hoge

Developer Relations Manager

Chris Hoge has spent over a decade working in open-source machine learning and infrastructure projects, where's he focused on building vibrant developer and user communities. He has an M.S. in Applied Mathematics from the University of Colorado, where he studied numerical methods for simulating physical systems. He lives in the beautiful Pacific Northwest, where he spends his free time trail running and playing piano.



================================================================================

URL: https://www.modular.com/blog-all?topic=Popular

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog/what-ownership-is-really-about-a-mental-model-approach

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

May 29, 2024

# What ownership is really about:  a mental model approach

Ehsan M. Kermani

Ownership is a well-known concept in modern programming languages such as Mojo that aims to provide a safe programming model for memory management while ensuring high performance. This allows programmers to build safe abstractions without the need to manually manage memory, making development more efficient and less error-prone.Â

In this blog post, we will build an intuitive understanding of how memory works and expand on that foundation to create a simple mental model for the concept of ownership. This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

### An intuitive journey through memory management ð

To build a simple mental model for ownership, we first need to build an intuitive understanding of how memory works in computing.

#### Stack vs. heap

When a program runs, it needs a place to store data. This data is stored in two main areas of memory: the stack and the heap. Understanding the difference between these two is crucial for grasping the concept of ownership.Â

- Stack: The stack is a region of memory that stores temporary variables created by each function (including the main function). It is managed in a Last-In-First-Out (LIFO) order, meaning that the last variable pushed onto the stack is the first one to be popped off. The stack is very efficient for managing memory because allocating and deallocating variables on the stack is extremely fast. However, the stack is limited in size and scope. It's ideal for storing short-lived, simple data like function parameters, return addresses, and local variables.

- Heap: The heap is a larger pool of memory used for dynamic allocation. Unlike the stack, the heap does not follow a LIFO order. Instead, it allows for flexible and arbitrary memory allocation and deallocation. This makes it suitable for storing data that needs to live for an unpredictable duration or whose size may change. The trade-off is that managing heap memory requires more effort and can be slower due to the overhead of finding and managing free space.

We can visualize the stack and the heap in a simple linear model of memory as follows:

For example, the stack for the following Mojo program looks like:

We can zoom in each block of stack (i.e. stack frame) which includes a table of address, name and value. For simplicity, we do not take care of types and size of each stored value so the yâs address is 1.

A crucial difference between the stack and the heap is that two functions cannot access each other's stack frames, making the stack local to each function. This local nature ensures that variables stored on the stack are isolated and protected from unintended interference by other functions. In contrast, the heap is globally accessible, allowing functions to share and modify the same dynamically allocated memory. This global accessibility makes the heap suitable for storing data that needs to persist and be shared across various parts of a program. Such global access forms the core of memory management. Two functions can step into each otherâs heap memory, causing various issues that we will discuss later, and we will learn how ownership can prevent such memory management problems.

First, letâs see how we can access memory.

##### How to access memory?

We showed a variable storing a value has an address in memory. In C such address can be found using the address operator &. In the following example, &x returns the address of x in memory which is a (raw) pointer ptr.

To start building our mental model, we will use a Mojo-like pseudocode in this part to construct each piece that will eventually give rise to our mental model.

In this example, Address represents a simple structure holding an address, and RawPointer provides methods to allocate, write, read, and free memory. The main function demonstrates creating a variable x and obtaining its address as a RawPointer. We can visualize the stack as follows where -> x refers to the value of ptr in memory.

##### How to work with the heap?

So far, we have only worked with the stack. To work with the heap, letâs try to create a String object using our RawPointer. Note that the implementation details of String are not essential for our mental model, so we will skip them.

Looking closer, an instance of our String is created in these steps:

In this example:Â

- Allocate enough space on the heap for the String object. This is done by calling alloc on a RawPointer.Â

- Initialize the allocated space with the string "hello, world!". This is done by calling write on the uninitialized pointer.Â

- Assign the initialized pointer to the variable x, effectively creating a String object with the data stored on the heap.

The final result can be visualized as follows where the value of x is ptr but the actual value of x is the âhello, world!â stored in heap in binary format (below we also show the ASCII values for clarity):

Note that we also need to manually free the pointed memory after done with x. Next, letâs explore a few critical bugs that can occur with such manual memory access.

#### What are the critical bugs in manual memory management?

The following are the top two critical bugs that can happen in such manual memory management.

##### Use-after-free

A use-after-free error occurs when a program continues to use a pointer after the memory it points to has been freed. This can lead to Undefined Behavior (UB), such as program crashes or data corruption.

##### Double-free

A double-free error happens when a program attempts to free the same memory location more than once. This can lead to Undefined Behavior and potential security vulnerabilities.

We are now ready to build our intuitive mental model for the compiler to catch such mentioned bugs.

#### How to catch such memory management errors at compile time?

In order to help the compiler catch such errors, we start by including more metadata to our RawPointer representation.Â

##### Step 0: Add type parameter to RawPointer

As a statically compiled language, we first can enrich the RawPointer with the type information [T] as follows. Note that we are borrowing the compile-time parameter in square-brackets in Mojo

Adding type information [T] to RawPointer provides several benefits:Â

- Type Safety: By specifying the type of data the pointer is referencing, the compiler can ensure that only valid operations are performed on the data. This prevents type-related errors and ensures that operations like reading, writing, and freeing memory are type-safe.Â

- Compile-Time Checks: With type information, the compiler can perform more rigorous compile-time checks. This helps catch potential errors early in the development process, reducing runtime errors and improving the reliability of the code.Â

- Optimizations: The compiler can use type information to optimize memory access and management. Knowing the exact type of data being referenced allows the compiler to generate more efficient code.

##### Step 1: Add lifetime parameter to TypedRawPointer[T]

Next, we introduce a lifetime parameter to the TypedRawPointer[T] to track the validity period of pointer usage. This metadata tracks when a pointer is valid or has been freed.

With this, we have the following definitions

Definition 1: A reference is a typed raw pointer with lifetime. (Not be confused by the standard library Reference).

âDefinition 2: A reference is safe if its lifetime is in a valid state.Â

We should note that whenever we are done with checking lifetime, we can erase it (and its type too) to get to the underlying RawPointer.

With these added metadata, letâs go back and examine if they are enough to catch either use-after-free or double-free errors.

###### Lifetime analysis prevents double-free

Here is an example of how lifetime analysis prevents double-free errors.

First, memory heap is allocated. The allocation returns a raw pointer to an uninitialized memory. Next, a reference Ref is established and marks its lifetime as IsAlive. Finally, the data is written to the underlying memory with a variable x coming into existence.

The last line double_free(x) when expanded it becomes:

Visually it looks like:

By using lifetime parameters, we can ensure that memory is only freed once, preventing double-free errors.

###### Can lifetime analysis alone also prevent use-after-free?

Let's see if lifetime analysis alone can prevent use-after-free errors.

Â In the above example, the lifetime analysis ensures that the reference x is valid only within a specific time frame. After x.free(), the lifetime of the reference x is marked as IsDead, indicating that it should not be used. However, lifetime alone does not prevent other functions or parts of the code from using the reference x incorrectly (Lack of Exclusive Ownership). In the use_after_free function, x.write(tmp) is attempted even though x is already freed. Therefore, the lifetime system alone does not enforce exclusive access or prevent this usage. Lifetimes track validity but do not manage the state of the memory or enforce rules about how it can be accessed after certain operations. More metadata is needed to bake the ownership model which is necessary to ensure that once x.free() is called, no other references can access or modify x.

Looking at the above diagram, we might ask what if we disallow the use of x after its lifetime is marked IsDead? In that case, we will enter the dataflow analysis which keeps track of what value is in use, can or can not be used. This idea basically leads to the concept of ownership that we will talk about next.

##### Step 2: Specify ownership

To further enhance the safety of our memory management, we introduce ownership parameter. The goal of ownership is to enforce exclusive access and state management for memory references:

Such ownership parameter introduces a new layer of metadata that the compiler can use to enforce strict memory management rules:

- Exclusive Ownership: UniqueRef ensures that there is only one reference to the allocated memory at any given time. This prevents multiple references from accessing and potentially modifying the same memory simultaneously, avoiding race conditions and ensuring data integrity.

- Immutable Reference and Mutable References: ImmutableRef and MutableRef allow for controlled access to memory, ensuring that certain references can only read (immutable) while others can modify (mutable). This helps in maintaining the integrity of data by restricting unintended modifications.Â

- Invalid State: InvalidRef represents a state where the memory has been freed and should not be accessed after. This helps in catching use-after-free errors by marking references as invalid after the memory is deallocated.

Letâs have a look at the use-after-free case but now with ownership included

The use-after-free error is prevented by the UniqueRef type through the following mechanisms:

- Exclusive Ownership: UniqueRef ensures that x is the only reference to the allocated memory. Once x.free() is called, the memory is marked as freed, and the unique reference is invalidated i.e. turns into InvalidRef[UInt8, IsDead] vs. in the previous step 1, it was Ref[UInt8, IsDead]Â  which didnât have enough information to help the compiler reason about its ownership state.

- Compile-Time Checks: When use_after_free(x) is called after x.free(), the compiler checks the ownership state of x. Since x has been freed, it no longer holds a valid UniqueRef. The compiler can detect this invalid state and generate a compile-time error.Â â

- Lifetime and Ownership Enforcement: The combination of lifetime and ownership parameters ensures that the reference x cannot be used after it has been freed. This prevents any attempt to write to or read from the memory through x after the free operation.

#### Conclusion

In this first part of the series on ownership, we have developed a mental model of how memory works. We talked about the stack being used for local variables and function calls, while the heap is used for dynamic memory allocation. The stack is fast and local, whereas the heap is flexible and globally accessible which can cause critical memory errors such as use-after-free and double-free that can lead to Undefined Behavior and security vulnerabilities.

Later, we tried to look through the lens of the compiler and develop an intuitive understanding of how adding type and lifetime parameters to RawPointer helps the compiler catch errors related to type safety and memory validity. Last but not least, we introduced the ownership parameter that enforces exclusive access and state management, preventing use-after-free and double-free errors. By incorporating these concepts into our mental model, we enable the compiler to catch memory management errors at compile time, ensuring safer and more efficient memory usage. In the next blog post, we will dive deep into ownership in Mojo. We end this part with the following quote from the talk.

Additional resources:

- Get started with Mojo

- Official Mojo manualâ

- Join our Discord communityÂ

- Contribute to discussions on the Mojo and MAX GitHub

Report feedback, including issues on our Mojo and MAX GitHub tracker.

Until next time! ð¥

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Developer

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Ehsan M. Kermani

AI DevRel

Ehsan is a Seasoned Machine Learning Engineer with a decade of experience and a rich background in Mathematics and Computer Science. His expertise lies in the development of cutting-edge Machine Learning and Deep Learning systems ranging from Natural Language Processing, Computer Vision, Generative AI and LLMs, Time Series Forecasting and Anomaly Detection while ensuring proper MLOps practices are in-place. Beyond his technical skills, he is very passionate about demystifying complex concepts by creating high-quality and engaging content. His goal is to empower and inspire the developer community through clear, accessible communication and innovative problem-solving. Ehsan lives in Vancouver, Canada.



================================================================================

URL: https://www.modular.com/blog/fast-k-means-clustering-in-mojo-guide-to-porting-python-to-mojo-for-accelerated-k-means-clustering

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

May 20, 2024

# Fastâ¡k-means clustering in Mojoð¥: a guide to porting Python to Mojoð¥ for accelerated k-means clustering

Shashank Prasanna

One of the first things we teach our children growing up is how to group toys by colors, shapes, and sizes. Grouping objects into categories is a fundamental tool we use to understand the world around us. It's so important that we've developed several computational methods to find groups or patterns in data, and they are studied under a sub-field of machine learning called cluster analysis. There are several clustering algorithms, but k-means â the algorithm we're going to implement from scratch in Python and Mojoð¥ in this blog post â is one of the most popular due to its simplicity and ease of implementation.

In this blog post, Iâll:

- Explain what k-means is, how it works, and how to use it.

- Show you how to write the k-means clustering algorithm from scratch in Python and Mojo and highlight key differences between the two implementations.

- Guide you through code changes you'll need to make to port Python code to Mojoð¥ for significant performance benefits.

My goal is to introduce Mojoð¥ to Python developers using an example-driven workflow. This post will not make you a Mojoð¥ expert, but it will help you appreciate the similarities between Python and Mojoð¥ code, and how you can translate Python code to Mojoð¥ by introducing Mojoð¥ specific features like strong typing and vectorization to achieve substantial speedups over Python+NumPy code.

Get the code: All the resources shared in this blog post, including the complete implementation of k-means in Python and Mojo with detailed comments, scripts to run the examples, a benchmarking script, and utility functions that generate scatter plots (like the one below) and benchmark plots, are all available on GitHub:

The figure below shows clusters calculated by our Python+NumPy and Mojo implementations, within a synthetically generated dataset containing 3,000 samples (rows) and 100 features (columns), grouped into five categories. In the past, Iâve struggled to visualize 100 dimensional data (believe me, Iâve tried), so I reduced the data to two dimensions using Principal Component Analysis (PCA). The actual centroids, along with those calculated using each implementation (shared in this blog post), are also shown.

#### k-means performance and benchmarking

In this blog post, Iâll share some benchmark comparisons to show performance improvements of k-means in Mojoð¥ over Python+NumPy implementation. Performance can depend on many factors. In the plot below (click to zoom), I illustrate the speedup achieved by Mojoð¥ k-means over Python+NumPy k-means by varying the (1) number of clusters, (2) number of samples, and (3) number of features, while keeping other variables constant as specified in the plot. We observe speedups ranging from 6x to 250x with Mojoð¥ (click to zoom, best viewed when browser is full screen):

We'll discuss benchmarking in a little more detail at the end of this blog post, for now here are the key takeaways:

- Every workload is unique, so itâs hard to make generalized speedup statements, especially when comparing complex projects with small, specialized functions designed for benchmarking.

- For complex ML algorithms like k-means, several factorsâsuch as dataset size, number of clusters, number of iterations, and the effectiveness of the codeâcontribute to performance.

- Porting Python+NumPy code to Mojoð¥ will offer considerable speedups, since computationally expensive parts can easily be vectorized and parallelized.

But you really came here for the code, and I wonât keep you waiting. Letâs jump right into the implementation. First, a basic summary of the k-means algorithm and its detailsâitâs not complicated, I promise.

### What is k-means clustering?Â

Consider a tabular dataset with M rows and N columns, where each row represents an N-dimensional data point, and there are M data points in total. K-means is an iterative algorithm that assigns each data point to its nearest cluster based on its distance to the centroid of that cluster. The algorithm recalculates the centroids iteratively to reduce the average within-cluster distance. Upon convergence, similar data points are grouped together in common clusters.

For example, in an e-commerce dataset, each column might represent customer attributes such as past purchase history and demographic data, while each row represents a different customer. K-means clustering can help you cluster customers together and determine if there are common buying preferences within a cluster. You can use this information to recommend similar products and services to customers in the same cluster.

The algorithm is very simple. For a given dataset and desired number of clusters k.

- Initialization: Pick initial k centroids using k-means++ initialization algorithm (discussed below).Â

- Lloyd's iteration: Perform these steps till convergence:Assignment: For each data point calculate distance to each of the k centroids and assign the data point to the closest centroid.Update: For each cluster, recalculate the centroid by computing the mean of all data points assigned to that cluster.

- Assignment: For each data point calculate distance to each of the k centroids and assign the data point to the closest centroid.

- Update: For each cluster, recalculate the centroid by computing the mean of all data points assigned to that cluster.

- Assignment: For each data point calculate distance to each of the k centroids and assign the data point to the closest centroid.

- Update: For each cluster, recalculate the centroid by computing the mean of all data points assigned to that cluster.

In flow chart form:

### K-means class definition in Python and struct definitions in Mojo

Iâll discuss k-means++ algorithm, Lloydâs iteration and convergence criterion in more detail along with the Python and Mojo code below. Letâs now a look at the skeleton of both Mojo ð¥ implementation and Python implementation side by side (click to zoom, best viewed when browser is full screen):

At a high level you should observe:

- In Python we define a class and in Mojoð¥ we define a struct

- All the functions names are the same: __init__, distance_norm, _kmeans_plus_plus, fit

- Function arguments in Mojo are typed and they are not in Python.

- fit function have return type specified

- Some function arguments have a keyword inout in front of their name

If you are a seasoned Mojicianðªall this makes perfect sense to you. If you are a Python Wizard ð§, Mojo code might look very similar to typed Python code. Unlike Python however, Mojo is a compiled language and even though you can still use def for functions and omit types like in Python, Mojo lets you declare types so the compiler can better optimize code, and improve performance. We want to make k-means â¡fastâ¡ so I chose to use Mojoð¥ native features to speed it up. Both can be used the exact same way.Â

In Mojo:

In Python:

In scikit-learn:

Iâve written both the Python and Mojo versions to be very similar to k-means implementation in scikit-learn:

The terminal output when running all three using the run_kmeans.mojo file in GitHub is below.

For this specific problem configuration where n_clusters = 5, n_samples =Â  3000, and n_features =Â  100, Mojo is faster than both our Python implementation and scikit-learnâs implementation by 13 and 6 times respectively, with all of them converging close to a similar final inertia value. Scikit-learnâs implementation converges in only 3 iterations, and yet Mojo k-means is faster even when running for 3 extra iterations. If youâre wondering why scikit-learn converges in fewer iterations, it could be one of these reasons:

- Certain random initial centroids may be closer to the final centroid, and therefore help converge sooner.

- Scikit-learnâs k-means implements multiple convergence criteria, whereas our implementation only checks for change in inertia value.

Now letâs compare each section of the code side by side starting with class definition in Python and struct definition in Mojo and their initialization dunder method __init__(). We define all of k-means hyperparameters and algorithm options here (click to zoom, best viewed when browser is full screen). The red boxes show what changes we needed to make to port code over to Mojo.

To port the Python class definition over to Mojo struct, youâll need to make a few minor changes.Â

- __init__() is mostly unchanged. The only modifications youâll need to make is to add types to all arguments to __init__(). The variable self.centroids is an empty list in Python which can grow dynamically, whereas in Mojo structs are static and compile-time bound, so we declare self.centroids to be a list with a fixed capacity, equal to number of clusters.

- The struct definition itself differs from the Python class definition. We declare struct just like a Python class, but since structs are static and compile-time bound, parameterized by dtype which defaults to Float64. We also define struct Fields which are initialized in the __init__() function.

For more detailed comparisons between Mojo structs and Python classes see this documentation page.

### k-means fit() function in Python+NumPy and Mojo

Now, letâs move on to the fit() function which runs the k-means clustering algorithm (click to zoom, best viewed when browser is full screen). The red boxes show what changes we needed to make to port code over to Mojo.

The fit function implements this updated flow chart below. Iâve added line numbers (which are the same for Python and Mojo implementations), next to the corresponding step in the flowchart.

In the next section weâll learn more about each of these steps, for now letâs summarize the changes in fit() going from Python -> Mojo:

- We use fn instead of def for our struct methods. While Mojo supports the same dynamism and flexibility as a Python def function, fn functions provide strict type checking and additional memory safety, which can lead to faster performance.

- We add types to all function arguments and specify the return type.

- fn functions require variables to be declared with varÂ

- We replace NumPyâs np.abs and np.inf with Mojo standard libraryâs math.abs and math.limit.inf

Simple enough? Sure is! but let's address the elephant ðin the room: NumPy.

Python has NumPy, a powerful library for numerical computing that implements high-performance matrix operations. This library has been in development for more than 20 years and implements most of its high-performance routines directly in C to maximize hardware utilization. Mojo, on the other hand, is a younger language and lacks an equivalent high-performance matrix library.

I've implemented a Matrix data structure in Mojo, which provides rudimentary, NumPy-like basic matrix operations such as slicing, reductions, and element-wise mathematical operations in a fast, vectorized manner. Youâll see that all the NumPy references in the Python k-means code are replaced by Matrix in the Mojo k-means code. For example, in the function definition for fit(), you'll see that data has a type Matrix[dtype].

Note on convergence criteria (Lines 19 and 25 in the code excerpt): Since k-means is an iterative algorithm, we have to implement conditions to break from the iteration loop. In our example, we'll check for two convergence criteria: (1) the maximum number of iterations is reached, and (2) the change in inertia is below a certain threshold. In the k-means algorithm, inertia refers to the total sum of squared distances between each data point and the centroid of the cluster to which it is assigned.

### k-means plus plus initialization in Python+NumPy and Mojo

Since k-means is an iterative algorithm, we must start with some initial centroids and iteratively improve them. This also means the algorithm is sensitive to the initial selection of centroids, and poor initialization can lead it to converge to local minima, resulting in suboptimal clusters. Rather than starting with random centroids, the k-means++ algorithm provides better initial centroids that are more evenly distributed, resulting in faster convergence. Below are the Python and Mojo implementations of the k-means++ algorithm (click to zoom, best viewed when browser is full screen). The red boxes highlight the changes we needed to make to port the code over to Mojo.

Hereâs what's happening in the above code with corresponding line number in the code excerpt:

- [Line 8] Choose one data point uniformly at random from data as the first centroid

- [Line 13] Iteratively find remaining k-1 centroidssome text[Line 15] For each data point in data, compute squared Euclidean distance between the point and all found centroids[Line 17] Find the minimum distance to any centroid for each data point[Line 20 - 29] Choose one new data point at random as a new centroid, using a weighted probability distribution where a new centroid is chosen with probability proportional to squared Euclidean distance to centroid.Repeat till k initial centroids are foundÂ

- [Line 15] For each data point in data, compute squared Euclidean distance between the point and all found centroids

- [Line 17] Find the minimum distance to any centroid for each data point

- [Line 20 - 29] Choose one new data point at random as a new centroid, using a weighted probability distribution where a new centroid is chosen with probability proportional to squared Euclidean distance to centroid.

- Repeat till k initial centroids are foundÂ

- [Line 15] For each data point in data, compute squared Euclidean distance between the point and all found centroids

- [Line 17] Find the minimum distance to any centroid for each data point

- [Line 20 - 29] Choose one new data point at random as a new centroid, using a weighted probability distribution where a new centroid is chosen with probability proportional to squared Euclidean distance to centroid.

- Repeat till k initial centroids are foundÂ

Now letâs summarize the changes in _kmeans_plus_plus() going from Python -> Mojo:

- We again use fn functions instead of def.

- We declare all variables with var

- We declare temporary variables in Mojo for faster performance.

- We use random_si64 from the Mojo math standard library to replace NumPyâs np.random.randint

- We replace np.full in the Python code with Matrix[]() in Mojo the code

- We replace np.random.rand() in the Python code with random_float64() from the Mojo math standard library in the Mojo code

### Lloydâs iteration in Python+NumPy and Mojo

In the k-means algorithm, Lloyd's iteration performs two steps:

- Cluster assignment: For each data point in the dataset, calculate its distance to each centroid. Assign each data point to the cluster whose centroid is closest to it.

- Centroid updates: After assigning all data points to clusters, recalculate the centroids as the mean of all data points assigned to each cluster.

Below is Python and Mojo implementation of k-means plus plus algorithm (click to zoom, best viewed when browser is full screen). The red boxes show what changes we needed to make to port code over to Mojo.

Hereâs what's happening in the above code with corresponding line number in the code excerpt:

- [Line 5] For each data point in the dataset, calculate its distance to each centroid.

- [Line 7] Assign each data point to the cluster whose centroid is closest to it.

- [Line 10 - Line 12] Recalculate the centroids as the mean of all data points assigned to each cluster.

- [Line 14] Calculate inertia

Now letâs summarize the changes in _lloyds_iteration() going from Python -> Mojo:

- We added types to function arguments

- We declare all variables with var

- We replaced NumPy operations to calculate mean and sum with Matrix[]() equivalent in Line 12

Weâre at the home stretch here! Letâs take a look at our final function.

### Calculating squared Euclidean distance Python+NumPy and Mojo

The most computationally intensive part of the k-means algorithm is typically the cluster assignment step which involves calculating the distance between each data point and each centroid to determine which centroid is closest to each point. Accelerating the distance calculation can significantly speed up each Lloydâs iteration step and k-mean clustering as a whole.

While Python relies on NumPyâs fast implementation of Euclidean distance calculation implemented in C language and exposed via np.linalg.norm(). In Mojo weâll implement this from scratch by tapping into the full power of Mojoâs ease of use vectorization and parallelization support. In this section Python and Mojo code will look nothing alike (but if you are free to imagine a big blob of C code that you trust, has got your back).

Below is Python and Mojo implementation of k-means plus plus algorithm (click to zoom, best viewed when browser is full screen). The red boxes show what changes we needed to make to port code over to Mojo.

Hereâs what's happening in the above code with corresponding line number in the code excerpt:

- [Line 2] In the function definition we added the inout keyword infront of centroids_distance to receive a reference to the data structure that is mutable. i.e. changes to centroids_distance will be reflected outside the function, so we donât need to make copies of the data structure. NumPy arrays work this way by default in Python.

- [Line 7] defines a closure that calculates distance between all data points in data to the centroid/cluster number idx_centroid in the argument. Writing a function this way enables us to call Mojoâs parallelize function, so we can calculate centroid distances for each cluster in parallel.

- [Line 8] starts the outer loop that iterates over rows of data and calculates the distance between the data points in loop variable idx_mat_row and the current centroid in idx_centroid.

- [Line 11 - Line 19] includes the inner loops that calculate the squared norm between the data points in idx_mat_row of data and data points in centroid number idx_centroid, in a vectorized way. By operating on simd_width elements simultaneously, we can get huge speedup depending on the value of simd_width on your system. Since simd_width is a power of 2 we include a second loop on line 15 to processes the elements from floor(data.cols / simd_width) * simd_width to data.cols

Â Now letâs summarize the changes in distance_norm() going from Python -> Mojo:

- We implemented vectorized and parallelized squared Euclidean norm calculation from scratch.

- We leveraged SIMD (Single Instruction, Multiple Data) operations using special CPU registers that can process multiple bits of data at the same time.

Phew.

If you are a Python developer with no background in systemâs programming languages like C or C++ the code above was probably hard to follow. I could have alternatively written a much simpler looking loop that didnât take advantage of vectorization and parallelization to make it look like Python code, by trading off some performance. But therein lies the beauty of Mojo. You can write code that looks like Python for non-performance critical code and roll up your sleeves and write low-level code when performance matters. Mojoð¥ meets you where you are.

### Running and benchmarking both Python+NumPy and Mojo k-means implementations

The example on GitHub includes two files to test our implementations: run_kmeans.mojo and bench_kmeans.mojo.

#### run_kmeans.mojoÂ

This file shows you how to create some test data with a specified number of clusters and cluster it using Python+NumPy, Mojo and scikit-learn implementation of k-means. It also includes an option to generate 2-dimensional scatter plots of the 1st and 2nd principal components to visualize high-dimensional data. Here is a simple example of how to cluster some synthetic data with n_samples = 3000, n_features = 200 and n_clusters = 10 generated using scikit-learn's make_blobs function.Â

Partial output:

You can see that all three implementations converge to a similar final inertia value. You will, however, see run-to-run variations due to the inherent randomness of the k-means algorithm.

#### bench_kmeans.mojo

This file implements a very simple benchmarking setup to compare the performance of Python+NumPy, Mojo, and scikit-learn implementations of k-means. It also generates benchmarking plots shown below (click to zoom). To test the performance of both implementations, in bench_kmeans.mojo we specify the ranges of values to sweep for the benchmark. When we sweep one variable, we keep the other two constant and plot the results.

Note that the ranges and the choice of variable values that are held constant are arbitrary. Feel free to run bench_kmeans.mojo with different ranges and measure performance. Since the k-means algorithm is sensitive to random initial centroids, one implementation may converge sooner than the other, potentially running much quicker. To make the comparison fairer, we can set the k-means argument run_till_max_iter=True, which will force the algorithm to run until max_iterations even if the convergence criterion has been met.

Below, you can see plots of the time taken to run until the default max_iterations = 10 when sweeping cluster size, number of samples, and number of features. The values that are held constant are displayed at the bottom of each plot. For these ranges, we observe speedups ranging from 6x to 250x when using Mojoð¥. Each experiment runs for 10 full iterations with other convergence criteria disabled, providing a fair comparison of performance. In our implementation, I noticed that:

- In Plot 1, scaling the number of clusters results in higher speedups, since we compute the distance between data points and clusters much more frequently. Mojoð¥'s vectorized and parallelized code is significantly faster at computing distances.

- In Plot 2, scaling the number of samples shows a more consistent distribution, with a median speedup of approximately 150x.

- In Plot 3, scaling the number of features results in a decreasing speedup as the number of feature columns increases, due to the limited number of cores available on my MacBook Pro with M2 Pro. I expect that on a system with more cores, the speedup would be consistent.

Click to zoom, Best viewed when browser is full screen:

### Conclusion

I hope you enjoyed reading this primer on the k-means algorithm and how you can translate computationally intensive code from Python to Mojoð¥ for faster performance. Building an end-to-end example like this k-means clustering implementation is a great way to learn how to use a new programming language.Â

Please take this code, modify it, test it and run your own experiments with it. You can also use it to implement other ideas using the building blocks from this example. One cool example could be k-nearest neighbor search for Retrieval Augmented Generation (RAG) applications which also relies on fast euclidean Distance calculations. Join our Discord community and share your projects with other Mojiciansðª!

Thank you for reading! Here are some additional resources to get started.Â

- Get started with downloading Mojo

- Head over to the docs to read the programming manual and learn about APIs

- Explore the examples on GitHub

- Join our Discord community

- Contribute toÂ  discussions on the Mojo GitHub

- Read and subscribe to Modverse NewsletterÂ

- Read Mojo blog posts, watch developer videos and past live streams

- Report feedback, including issues on our GitHub tracker

Until next timeð¥!

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Developer

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Shashank Prasanna

AI Developer Advocate

Shashank is an engineer, educator and doodler. He writes and talks about machine learning, specialized machine learning hardware (AI Accelerators) and AI Infrastructure in the cloud. He previously worked at Meta, AWS, NVIDIA, MathWorks (MATLAB) and Oracle in developer relations and marketing, product management, and software development roles and hold an M.S. in electrical engineering.



================================================================================

URL: https://www.modular.com/blog-all?topic=Developer

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog/whats-new-in-max-24-4-max-on-macos-fast-local-llama3-native-quantization-and-gguf-support

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

June 25, 2024

# What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

Ehsan M. Kermani

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

In this blog post, we will explore various features of MAX Pipelines and their benefits for GenAI applications such as native GGUF, tokenizer and quantization support. To follow along, please ensure you have installed MAX 24.4. Note that MAX now ships with Mojo for all platforms. If you previously installed a standalone version of Mojo, you should uninstall it and install MAX instead. After installing MAX, you should get the following hash release when running max -v

### Get Started with MAX Pipelines

Letâs start with running quantized Llama3 pipeline in pure Mojo locally as follows

On my M3 MacBook Pro, it outputs

Fantastic ð now we are ready to explore more features.

#### Native GGUF Support in Mojo

âGGUF has become a standard file format for storing models for inference workload and specially is suitable for single-file deployment of LLMs. MAX Pipelines natively supports gguf in Mojo. GGUF specification is as follows

Our native implementation parses GGUF file format in preparation for inference ensuring optimal performance and integration within MAX Pipelines. This feature is fully integrated, allowing developers to efficiently load and utilize GGUF models without additional configuration.

#### Native Tokenizer Support in Mojo

MAX Pipelines provides native Mojo support for tokenizer, enabling efficient text preprocessing directly within your Mojo applications. This integration offers several benefits, ensuring that your Generative AI models can handle natural language input with high performance and accuracy.

#### Quantization Support

Quantization is a well-known technique to reduce memory and computational costs of running deep learning models. MAX Pipeline supports llama.cpp quantization encoding such asÂ

- Q4_0: 4-bit block quantization where weights are divided in blocks of 32 weights. The scale of each block is stored as float16 format.

- Q4_K:Â  4-bit quantization in super-blocks containing 8 blocks, each block having 32 weights. The scale and minimum of each super-block are stored as float16. The scales and minimums of each block in a super-block are quantized with 6-bits. On average, this ends up using 4.5 bits-per-weight.

- Q6_K: 6-bit quantization in super-blocks containing 16 blocks, each block having 16 weights. The scale of each super-block is stored as float16. The scale of each block is quantized with 8 bits. On average, this ends up using 6.5625 bits-per-weight.

Here is a visual demonstration of these quantization schemes

We generally recommend using Q4_K for the best performance and memory use. The higher bit quantization encoding such as Q6_K is useful when we want to trade more memory for higher accuracy.

These advanced quantization approaches are typically only supported in specialized AI frameworks like llama.cpp, but MAX makes them accessible to a much wider range of models very easily. For more details, please refer to llama.cpp here.

The run_pipeline.ð¥ Â comes with various CLI options such as changing the quantization encoding (default is Q4_K) as follows

which outputs

#### MAX Pipeline Seamless Integration with PyTorch and HuggingFace

MAX Pipeline integrates seamlessly with the PyTorch and HuggingFace tokenizer as well. allowing developers to leverage the powerful tools and libraries from these ecosystems. This integration ensures that you can build and deploy advanced AI models using familiar frameworks while benefiting from the performance and efficiency enhancements provided by MAX.Â

For example, in Llama2 pipeline, we proceed by installing the transformers package via

and we can use it with the llama2 option

where it generates

#### MAX Graph Custom Operator

âMAX Graph API enables creating custom operators which is useful for writing highly customized graph level operators. Particularly for Llama2, MAX Pipelines has a custom Rotary Embedding (RoPE) operator here. We can invoke such custom operator via

which gives us

To learn more about MAX Graph Custom Operator, please visit our MAX Graph API Tutorial.

#### Example API

We are iterating rapidly on the MAX Pipelines to deliver best-in-class APIs for MAX developers. Our goal is to make MAX Pipelines more accessible, powerful, and easy to use. One such example can be obtained from here which showcases the capabilities and ease of use of MAX Pipelines, ensuring that developers can quickly integrate and benefit from our advancements. Below is an example to illustrate this

which generates this joke

We can also use the integrated tokenizer and tokenize our prompt with

outputs

By following these simple steps, developers can take full advantage of the advanced features provided by MAX Pipelines. This example API is designed to be intuitive and developer-friendly, allowing for quick integration and immediate productivity.

### Next Steps

We are very excited to see what you can accomplish using MAX Pipelines. Please share your creations and innovations with the community. Here are a few options to get you started:Â

- Explore the various examples and templates provided in the MAXÂ repository.Â

- Experiment with different quantization formats and tokenizers to optimize your models.

- Utilize the GGUF file format for efficient model storage and deployment.Â

- Share your projects and experiences on forums, social media, and the Modular AI community.Â

- Use MAX Pipelines to build state-of-the-art AI models tailored to your specific needs. Deploy your models on various platforms, including macOS, Intel x86, and ARM Graviton cloud-serving infrastructure. Leverage the performance enhancements of the Quantization API to optimize your Generative AI pipelines.

### Conclusion

The release of MAX 24.4 is a great progress on the unification of AI development tools. With the introduction of MAX on macOS and MAX Pipelines featuring native support for Generative AI models such as Llama3, developers now have unprecedented capabilities to build and deploy advanced AI models efficiently. This release brings together powerful features like the Quantization API, native GGUF, and tokenizer support, providing a comprehensive toolchain for creating high-performance AI solutions.Â

Throughout this blog post, we have explored various features of MAX Pipelines and their benefits for Generative AI applications. From running quantized Llama3 models in pure Mojo to leveraging the integrated tokenizer and quantization support, MAX Pipelines offer a robust and flexible framework for AI development.Â

#### Key Takeaways for Developers

- Native GGUF Support: Simplifies model storage and deployment with efficient single-file format.

- Integrated Tokenizers: Ensures efficient text preprocessing and seamless integration with Generative AI models.Â

- Quantization Support: Reduces memory and computational costs, enabling the deployment of large models on local machines.Â

- Seamless Integration: Works effortlessly with PyTorch and HuggingFace, allowing the use of powerful tools and libraries from these ecosystems.Â

- Custom Operators: Provides the ability to create highly customized graph-level operators, enhancing model performance and flexibility.Â

By following the steps and examples provided, developers can take full advantage of the advanced features in MAX Pipelines, ensuring optimal performance and ease of use. Whether you're working on macOS, Intel x86, or ARM Graviton cloud-serving infrastructure, MAX 24.4 empowers you to build state-of-the-art AI models tailored to your specific needs.

Weâre excited to see what you build with MAX 24.4 â¡ï¸ and Mojo ð¥!

- Get started with MAX 24.4.

- Read the MAX â¡ï¸ docs and the Mojoð¥ manual.

- Learn about the new quantization APIs.

- Explore the MAX pipeline examples on GitHub.

- Join our Discord community.

- Read and subscribe to Modverse Newsletter.

- Read Mojo blog posts, watch developer videos and past live streams.

Until next time! ð¥

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Developer

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Ehsan M. Kermani

AI DevRel

Ehsan is a Seasoned Machine Learning Engineer with a decade of experience and a rich background in Mathematics and Computer Science. His expertise lies in the development of cutting-edge Machine Learning and Deep Learning systems ranging from Natural Language Processing, Computer Vision, Generative AI and LLMs, Time Series Forecasting and Anomaly Detection while ensuring proper MLOps practices are in-place. Beyond his technical skills, he is very passionate about demystifying complex concepts by creating high-quality and engaging content. His goal is to empower and inspire the developer community through clear, accessible communication and innovative problem-solving. Ehsan lives in Vancouver, Canada.



================================================================================

URL: https://www.modular.com/blog/max-24-4-introducing-quantization-apis-and-max-on-macos

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

June 7, 2024

# MAX 24.4 - Introducing quantization APIs and MAX on macOS

Modular Team

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

Release highlights:

- New Quantization API for MAX Graphs: Reduce LLM inference latency by 7x compared to llama.cpp context encoding through decreased memory usage and improved performance with the new Quantization API. MAX Graph supports BF16, INT4, and INT6 quantization, including K-Quantization.

- Llama on MAX: MAX 24.4 includes new implementations of Â Llama 3 and Llama 2, demonstrating the full power of MAX Graphs and the Quantization API!

- MAX on macOS: Everything you love about MAX is now available on Apple silicon, further enhancing MAXâs programmability and portability. Developers can build state-of-the-art AI pipelines locally and seamlessly deploy them to cloud systems like Intel x86 and ARM Graviton.

- Mojo ð¥ Improvements: Mojo 24.4 features several performance improvements to the core language and standard library. For the core language, def functions are more flexible, and advanced users will appreciate more advanced loop unrolling features, the ability to return safe references, and many others.

- Community-Driven Innovation: Mojo 24.4 features 214 community pull requests from 18 contributors, with 30 contributed features accounting for 11% of all improvements in the latest Mojo release. These include performance and quality-of-life improvements to the standard library collections, new data and filetype operations, and updates to SIMD bitwise operations. You can find a complete list of contributors and enhancements in the Mojo 24.4 release notes.

You can get started with MAX 24.4 now through the Modular developer console or by installing it directly from your terminal:

Head over to the MAX documentation for complete instructions on installing or updating MAX.

### Quantization API

The new MAX Quantization API is a huge step in bringing state-of-the-art performance to models built with MAX Graphs.

Why does quantization matter?

Token generation in LLMs is memory-bound, and reducing the size of the model weight from FP32 to INT4 proportionally improves performance without significantly impacting model quality. Reduced model size also decreases the hardware requirements for running LLMs, making models more widely available and cost-effective to run.

MAXâs quantization API makes transitioning from full precision to INT4 quantization easier, a massive win for the MAX and Mojo developer communities.

### New Quantized Llama Models

To highlight the power of MAXâs quantization API, weâre releasing two new quantized LLMs as part of the MAX 24.4 release: Llama 3 and Llama 2. These models are built entirely in Mojo ð¥ using the MAX Graph API. These models are the first of a series to meet a need for State-Of-The-Art LLMs that are performant and portable across all CPU types.

You can download Llama 3 now and try it out!

Read more in the MAX Getting Started guide.

### MAX on MacOS

MAX is now available for macOS, delivering the full suite of acceleration and inference APIs to Apple silicon. This includes the new, fully quantized Llama3 model, which has a more than 8x performance boost in context encoding using INT4 compared to F32.

Developers can seamlessly transition from building SOTA models on their development machines to putting them into production on Intel x86 and ARM Graviton cloud-serving infrastructure. Weâre excited to expand the portfolio of hardware platforms supported by MAX, delivering on the promise of programmability and portability. You can get started with MAX on macOS today!

### New Developer Resources

To support our growing community of developers and users, weâve completely reworked our documentation to focus on the user journey with MAX. Thereâs now a single API reference to cover the entire MAX platform and a new Getting Started guide that makes it faster and easier to get MAX up and running. This new experience makes it easier to understand and use all of MAXâs capabilities, and weâre excited to see what the Mojo and MAX communities will build!

In addition to the refreshed docs, weâre excited to announce Modular AI resources: a centralized hub for the latest and most relevant research papers on LLMs, Generative AI, and optimized ML systems.

### ð Get Started with MAX 24.4!

Download MAX 24.4 now to get started with the new MAX Graph Quantization API, and start accelerating your models now. Read the docs to learn more, and check out our examples on how to run llama3 with the MAX Engine.

Weâre excited to see what you build with MAX 24.4 â¡ï¸ and Mojo ð¥!

- Get started with MAX 24.4.

- Read the MAX â¡ï¸ docs and the Mojoð¥ manual.

- Learn about the new quantization APIs.

- Explore the MAX pipeline examples on GitHub.

- Join our Discord community.

- Read and subscribe to Modverse Newsletter.

- Read Mojo blog posts, watch developer videos and past live streams.

Until next time! ð¥

ð Special thanks to our community contributors: @rd4com, @toiletsandpaper, @helehex, @artemiogr97, @mikowals, @kernhanda, @lsh, @LJ-9801,@YichengDWu, @gabrieldemarmiesse, @fknfilewalker, @jayzhan211, @martinvuyk, @ChristopherLR, @mzaks, @bgreni, and @Brian-M-J.

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Product

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Modular Team

Company

Our mission is to have real, positive impact in the world by reinventing the way AI technology is developed and deployed into production with a next-generation developer platform.



================================================================================

URL: https://www.modular.com/blog/max-24-3-introducing-max-engine-extensibility

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

May 2, 2024

# MAX 24.3 - Introducing MAX Engine Extensibility

Modular Team

Today, weâre thrilled to announce the launch of MAX 24.3, highlighting a preview of the new MAX Engine Extensibility API that allows developers to unify, program, and compose their AI pipelines on top of our next-generation compiler and runtime stack for best-in-class performance.

MAX Engine is a next-generation compiler and runtime library for running AI inference. With support for PyTorch (TorchScript), ONNX, and native Mojo models, it delivers low-latency, high-throughput inference on a wide range of hardware to accelerate your entire AI workload. Furthermore, the MAX platform empowers you to harness the full potential of the MAX Engine through the creation of bespoke inference models using our MAX Graph APIs.

In this release, we continue to build on our incredible technology foundation to improve programmability of MAX for your workloads with 24.3 features including:

- Custom Operator Extensibility: Write custom operators for MAX models using the Mojo programming language for intuitive and performant extensibility.

- Mojo ð¥ Improvements: The Mojo language and standard library continues to mature, with key improvements that will be welcomed by Python experts. This includes enhancements to built-in types like Tuple and support for in function types for both optional and variadic arguments. Read the Whatâs New in Mojo 24.3 blog post and check out the complete list of changes in the Mojo 24.3 changelog.

- Less Dependencies and Smaller Package Size: TensorFlow support has been removed from the standard MAX package, making it 60% smaller, resulting in faster download times and fewer dependencies. TensorFlow is still available for enterprise users. Contact us for more information.

- Community-Driven Innovation: Following on open sourcing the Mojo standard library, this release includes the first community-submitted PRs to the Mojo standard library â featuring 32 significant community contributions, improving on the built-in types and usability of the Mojo standard library. Together with our amazing community, weâre shaping the future of AI development!

### Custom Operators

One of the major features of the MAX 24.3 release is a preview of the ability to easily work with custom operations when building AI pipelines. If youâre new to custom operations in AI pipelines, they refer to an operations that you define and implement on your own. This matters a lot when you want to build novel mathematical operations or algorithms that might not ship out-of-the-box in AI frameworks like PyTorch or ONNX, if you need to optimize performance for specific tasks, or if you need to utilize hardware accelerations that are not natively supported in these frameworks.

Writing custom operations in frameworks like PyTorch and ONNX is challenging because:

- They are far too slow when implemented in Python.

- Implementing them in low-level languages like C++ and CUDA requires deep expertise.

- Thereâs no guarantee your custom operation will work consistently across different hardware platforms.

- Maintaining custom ops to work with new framework updates is incredibly challenging.

In addition to all this, the API surfaces that you have to develop with to implement custom operations are incredibly ugly when implementing, registering, building and executing your workload. The traditional AI stack is fragmented and slows down AI innovation for everyone.

It doesnât have to be this hard

Imagine a world where you can write a custom operation for your AI workload that seamlessly executes across hardware, compiles cleanly, and generates platform independent packages. It's as easy as writing, packaging and executing your op â and that's it. And rather than tell you, letâs see how easy it is using an example from our docs. Let's start by adding a custom op like the Det op since this op is currently not supported in MAX Engine so any ONNX model using this op fails to compile. To add it, we start by writing the op in Mojo:

To package the custom op, create a directory that includes the above Mojo code, plus an empty __init__.mojo file. Then, pass that directory name to the mojo package command like below:

And then all we have to do is load our ONNX model into MAX Engine with the custom op and run inference with the Python API:

And boom ð¥ the ONNX model with our new op is optimized and executing:

And that's not all â¦.

Not only can you add custom operators to existing models like ONNX and Pytorch, you can also write your own MAX Graphs with custom operation extensions using Mojo - a much cleaner, more performant and more dependency free offering. We have made the whole process of building and implementing a custom operation much easier for everyone with MAX Graphs as a completely clean approach. You can also benefit when using MAX Graph API:

- AI Pipelines Unification: Centralize all your AI workflows with a single source of truth. By utilizing Mojo for authoring kernels, developers write kernels once and can reuse them across different ML frameworks by simply adding one annotation ensuring seamless integration and fewer discrepancies.

- Accelerated Development Cycle: Experience a smoother, faster development process with no need for traditional build processes, linkers, or C++ compilers. MAX Engine simplifies your workflow, allowing you to iterate and deploy rapidly.

- Built-in Performance Optimization: Leverage automatic kernel fusion and graph optimizations with the MAX Engine runtime for free, letting you focus on building your AI pipelines rather than optimization.

- Zero Cost Abstractions: MAX Engine and Mojo being built on top of MLIR take full advantage of modern compilers. Your code is inlined and participates directly in the compiler's optimization phases, enhancing efficiency and reducing overhead.

- Portability: MAX Engine ensures that your Mojo code is portable across diverse platforms and hardware setups, as long as they support LLVM or MLIR. This provides hardware optionality and frees you from platform-specific constraints, broadening your deployment options.

Check out some MAX Graph API examples here - we have a lot more incredible additions coming to MAX Graphs soon.

### Download MAX 24.3 Now ð

MAX 24.3 is just the beginning of some incredible updates we're delivering on our MAX roadmap, with MacOS support and Quantization coming soon and GPUs coming this summer. Head over to the Modular Developer Portal now to download MAX 24.3 and get started. Read the docs to learn more, check out our examples to learn how to build custom operators for the MAX Engine, and share your feedback on how we can improve upon this preview release.

Weâre excited to see what you build with MAX 24.3 and Mojo. Get started with MAX and Mojo now!

- Download MAX and Mojo.

- Head over to the docs to read the Mojoð¥ manual and learn about APIs.

- Explore the extensibility examples on GitHub.

- Join our Discord community.

- Contribute to Â discussions on the Mojo GitHub.

- Read and subscribe to Modverse Newsletter.

- Read Mojo blog posts, watch developer videos and past live streams.

- Report feedback, including issues on our GitHub tracker.

Until next time! ð¥

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Product

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Modular Team

Company

Our mission is to have real, positive impact in the world by reinventing the way AI technology is developed and deployed into production with a next-generation developer platform.



================================================================================

URL: https://www.modular.com/blog/max-24-2-is-here-whats-new

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

March 28, 2024

# MAX 24.2 is Here! Whatâs New?

Modular Team

Today, weâre excited to announce general availability of MAX 24.2, with several big features.

First, we're thrilled to share the next evolution of Mojo ð¥, open sourcing the Mojo standard library. Open sourcing the standard library gives our growing developer community an active voice in its development, and is an invitation to contribute directly to Mojo. As part of this commitment and to facilitate community development, weâre also releasing Mojo nightly builds that include the latest language development features. The open source release of the Mojo standard library is available now on GitHub.

Second, MAX 24.2 ships with significant enhancements to the MAX engine and the Mojo programming language. MAX 24.2 and Mojo builds are available directly through the Modular command line interface.

â¡Read on to learn more!

## Open sourcing the Mojo standard library

At Modular, we believe that the future of AI and machine learning must be open source. Since its launch in May of 2023, weâve been on a path to gradually open source Mojo and core parts of the MAX platform, and today we are open sourcing the Mojo Standard Library under the Apache 2 license on GitHub. This release is a big deal - including nightly builds, a new contribution model, and a roadmap for the standard library. Â Beyond providing access to the code, we're opening the development processes. For more information on what's next and how to contribute, read our detailed post: âThe Next Big Step in Mojo ð¥ Open Source.â

## MAX Engine enhancements ðï¸

The most significant new feature in MAX 24.2 is support for dynamic tensor input shapes in TorchScript models. This addition to MAXâs TorchScript capabilities greatly increases the usability of MAX-compiled models, especially for applications like LLMs where the entire input size isnât known at compile time. Additionally, dynamic input shapes make it possible to run with dynamic batch sizes and removes the need for input padding, offering performance improvements to your inference pipeline.

Learn more about the updates to MAX 24.2 in the article âLeveraging MAX Engine's dynamic shape capabilities.â

## New features in Mojo ð¥

MAX 24.2 ships with the latest version of Mojo, which includes a number of language improvements. One of the biggest features that will make Python fans rejoice is the addition of keyword arguments to the Python integration layer. This feature greatly simplifies mixing Mojo and Python, and vastly improves the use of popular libraries like matplotlib.

matplotlib

Following more closely to the Python convention of duck typing, structs and other nominal Mojo types are now allowed to implicitly conform to traits. This makes it much easier to write new types that can take advantage of built-in traits like Stringable, simplifying your code while still giving Mojo's strong assurances of flexibility, safety, and performance of compile-time traits.

Stringable

The Mojo changelog contains a list of improvements showcasing all the Mojo language improvements and additions.

## Get started

MAX 24.2 and Mojo nightly builds are available using the modular tool. To get started, download the Modular CLI from the Modular developer portal, which you can use to install the MAX 24.2 release and the latest Mojo nightly build.

modular

### Install MAX 24.2

MAX 24.2 is available for Linux on x86 and ARM platforms. You can install it with: modular install max

modular install max

You can upgrade with: modular upgrade max

modular upgrade max

### Install Mojo nightlies

Mojo nightlies are available for Linux on x86 and ARM platforms and MacOS. You can install them with: modular install nightly/mojo

modular install nightly/mojo

## Join the Modular community!

To learn more about MAX, and get involved with the Modular community:

- Download MAX 24.2 and Mojo.

- Read more about âThe next big step in Mojo ð¥ open source.â

- Learn about MAX 24.2 support for TorchScript in âLeveraging MAX Engine's dynamic shape capabilities.ââ

- Connect with users and devs in our Discord community.

- Report feedback, including issues on our MAX and Mojo GitHub tracker.

We're thrilled to share this latest release of MAX with you. Building a language and its infrastructure is hard work and takes time, and weâre excited to invite developers worldwide to help shape the development of Mojo and the future of AI infrastructure!

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Product

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Modular Team

Company

Our mission is to have real, positive impact in the world by reinventing the way AI technology is developed and deployed into production with a next-generation developer platform.



================================================================================

URL: https://www.modular.com/modverse/modverse-weekly-41

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

August 16, 2024

# Modverse Weekly - Issue 41

Jack Clayton

The Mojo Community Meeting has been renamed to the MAX + Mojo Community Meeting. We explained how we combined Mojo and MAX, and updated the license for both to ensure you can be confident building free (and open source) technologies, as well as using Mojo and MAX for your own commercial projects. We also shared our plans to remove auth from the download experience for Mojo and MAX, the availability of conda packages for easy integration into your applications, and a new magic CLI which makes it all reliable and lightning fast.

### Blogs, Tutorials, and Videos

- Check out the 6th Max + Mojo Community MeetingâSoren presented his fantastic Mojo bindings to DuckDBÂ which shows how you can use Mojo's strong type system to interop with a C++ library.Gabriel gave an insightful presentation on small string optimizations in Mojo

- Soren presented his fantastic Mojo bindings to DuckDBÂ which shows how you can use Mojo's strong type system to interop with a C++ library.

- Gabriel gave an insightful presentation on small string optimizations in Mojo

- Modular was listed as one of the most promising startups of 2024 on Business Insider

- Learn about how to deploy a MAXÂ model on AWSÂ with SageMaker and CloudFormation

- EKBÂ PhD published a video on Mojo's dictionary performance improvements

- Soren presented his fantastic Mojo bindings to DuckDBÂ which shows how you can use Mojo's strong type system to interop with a C++ library.

- Gabriel gave an insightful presentation on small string optimizations in Mojo

### Awesome Mojo

You can discuss these projects with the creators in the Discord forum:Â # community-showcase

- Frank released libcurl bindings for Mojo

- thatstoasty has a gist for reading from stdin in Mojo. We'll add this to the stdlib soon.

- Helenex released a geometric algebra generator named infrared.

- Jensen released a TCP framework with a server and client named FireTCP

- rd4com created a repo for generating Neovim themes in realtime

### Open Source Contributions

Check out all the open source contributions here. Â make sure to DM Jack Clayton on Discord if you've had your first PRÂ merged to claim some epic Mojo swag!

Check out the merged contributions this week from our valuable community members:

- soraros[stdlib] Remove String constructor from PythonObject[stdlib] Use infer only in bit

- [stdlib] Remove String constructor from PythonObject

- [stdlib] Use infer only in bit

- gabrieldemarmiesse[stdlib] Use memcpy in List._realloc for x13.2 speedups in some benchmarks

- [stdlib] Use memcpy in List._realloc for x13.2 speedups in some benchmarks

- pythoncrazy[Docs] Clarify the rounding in the builtin math module.

- [Docs] Clarify the rounding in the builtin math module.

- jlofti[stdlib] Added example to Powable docstring

- [stdlib] Added example to Powable docstring

- helehex[stdlib] Move InlineArray into collections module[stdlib] Fix TODO in /test_string.mojo

- [stdlib] Move InlineArray into collections module

- [stdlib] Fix TODO in /test_string.mojo

- msaelices[stdlib] Fix TODO item about using os.path.split() in tempfile test

- [stdlib] Fix TODO item about using os.path.split() in tempfile test

- [stdlib] Remove String constructor from PythonObject

- [stdlib] Use infer only in bit

String

PythonObject

bit

- [stdlib] Use memcpy in List._realloc for x13.2 speedups in some benchmarks

List._realloc

- [Docs] Clarify the rounding in the builtin math module.

- [stdlib] Added example to Powable docstring

Powable

- [stdlib] Move InlineArray into collections module

- [stdlib] Fix TODO in /test_string.mojo

InlineArray

/test_string.mojo

- [stdlib] Fix TODO item about using os.path.split() in tempfile test

## Sign up for our newsletter, Modverse

Get all our latest news, announcements and updates delivered directly to your inbox. Unsubscribe at anytime.

Thank you for your submission.

Your report has been received and is being reviewed by the Sales team. A member from our team will reach out to you shortly.

Thank you,

Modular Sales Team

## Next blog post:

- Modverse Weekly - Issue 40August 1, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

Modverse Weekly - Issue 40

August 1, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Modverse Issues

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Jack Clayton

AI Developer Advocate

Jack started his career optimizing autonomous truck software for leading mining companies, including BHP and Caterpillar. Most recently he was designing computer vision software, putting AI inference pipelines into production for IDVerse. He is enormously passionate about the developer community, having been a Rust, Go, Python and C++ developer for over a decade. Jack enjoys making complicated topics simple and fun to learn, and heâs dedicated to teaching the world about Mojo ð¥.



================================================================================

URL: https://www.modular.com/modverse/modverse-weekly-40

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

August 1, 2024

# Modverse Weekly - Issue 40

Jack Clayton

Last week we began alpha testing our new magic CLIÂ for installing Mojo and MAXÂ through the Conda ecosystem. It allows you to pin Mojo/MAXÂ to different versions, and add Python dependencies so you can reliably share your Mojo and MAXÂ projects. If you want to try it out before release, you can find the doc for installing it here.

We also launched a new tutorials page for our docs, our first tutorials are:

- Deploy a model with Kubernetes and Helm

- Deploy a model with Amazon SageMaker and AWS CloudFormation

- Get started with MAX Graph

- Create a custom op for an ONNX model

- Run an ONNX model with Python

### Blogs, Tutorials, and Videos

- Talha published a LinkedIn blog on implementing Yolo in pure Mojo

- Learn about Debugging in Mojoð¥

- Chris Lattner presented at the AIÂ Engineer World's Fair: Unlocking Developer Productivity across CPU and GPU with MAX

- Chris Lattner sat down with Primeagen to discuss Mojo and the history leading him into building the language.

- Check out the Mojo Community Meeting #4 and #5

- Read about our internal tool we made public for managing a stack of PRs: stack-pr

### Awesome Mojo

You can discuss these projects with the creators in the Discord forum:Â # community-showcase

- TiliFe released the first ML framework with autodiff built on top of MAX: Endia. Check out how familiar the API is to both PyTorch and JAX developers (click the GIF to enlarge it):

- Frank released Mojo FFI bindings to blend2d

- Frank also released image manipulation libraries for Mojo: box_blur_mojo, haldcut-mojoâ

- Soren released Mojo FFI bindings to DuckDBâ

- Dmitry released a Mojo library for calling into io_uring on x86-64 linux: io_uringâ

- Krisztian built an Apache Arrow implementation in Mojo: fireboltâ

- Krisztian also released a GitHub action for installing Mojo: setup-mojoâ

- Ethan released a very fast MatMul implementation that outperforms Numpy in pure Mojo: matmul.mojoâ

- Toasty released a bound logger library for Mojo: stump

- Melody released a devcontainer for setting up Mojo with Neovim: mojo-nvim-devcontainer-cli

### Open Source Contributions

Check out all the open source contributions here. Â make sure to DM Jack Clayton on Discord if you've had your first PRÂ merged to claim some epic Mojo swag!

Check out the merged contributions this week from our valuable community members:

- leandrolcampos[stdlib] Add has_fma function to sys package

- [stdlib] Add has_fma function to sys package

- helehex[stdlib] Add equality methods to Reference

- [stdlib] Add equality methods to Reference

- sorarosâ[stdlib] Use inferred-only in rebind[stdlib] Fix argument convention for iota[stdlib] Cleanup polynomial.mojo, improve docstring[stdlib] fix math.gcd for negative numbers[stdlib] Simplify several methods in the UnsafePointer struct[stdlib] Modernise Span.__[eq/ne]__ with latest conditional conformance support[stdlib] Move to EqualityComparableCollectionElement

- [stdlib] Use inferred-only in rebind

- [stdlib] Fix argument convention for iota

- [stdlib] Cleanup polynomial.mojo, improve docstring

- [stdlib] fix math.gcd for negative numbers

- [stdlib] Simplify several methods in the UnsafePointer struct

- [stdlib] Modernise Span.__[eq/ne]__ with latest conditional conformance support

- [stdlib] Move to EqualityComparableCollectionElement

- gabrieldemarmiesse[stdlib] Rename _get_reference_unsafe to unsafe_get in InlineArray[stdlib] Move find() from StringRef to StringSlice[stdlib] Add a few explicit copy constructors[stdlib] Abort on list resize to bigger sizes without fill value

- [stdlib] Rename _get_reference_unsafe to unsafe_get in InlineArray

- [stdlib] Move find() from StringRef to StringSlice

- [stdlib] Add a few explicit copy constructors

- [stdlib] Abort on list resize to bigger sizes without fill value

- jjvraw[stdlib] Remove obsolete FIXME[stdlib] Extend tests for SIMD.__pow__[stdlib] Remove _div_ceil_positive in favour of math.ceildiv[stdlib] Remove duplicate test from PR #3279[stdlib] Allow !r conversion flag in String.format[stdlib] Fix atol parsing for prefixed integer literals with leading underscores[stdlib] __contains__ for ListLiteral

- [stdlib] Remove obsolete FIXME

- [stdlib] Extend tests for SIMD.__pow__

- [stdlib] Remove _div_ceil_positive in favour of math.ceildiv

- [stdlib] Remove duplicate test from PR #3279

- [stdlib] Allow !r conversion flag in String.format

- [stdlib] Fix atol parsing for prefixed integer literals with leading underscores

- [stdlib] __contains__ for ListLiteral

- mzaks[stdlib] Add string justify methods

- [stdlib] Add string justify methods

- shayan-shams[docs] Add str() in print_many() in Functions

- [docs] Add str() in print_many() in Functions

- vguerra[stdlib] UTests for SIMD's operators required by Comparableâ[stdlib] UTests for FloatLiteral's operators required by Comparable

- [stdlib] UTests for SIMD's operators required by Comparableâ

- [stdlib] UTests for FloatLiteral's operators required by Comparable

- [stdlib] Add has_fma function to sys package

has_fma

sys

- [stdlib] Add equality methods to Reference

Reference

- [stdlib] Use inferred-only in rebind

- [stdlib] Fix argument convention for iota

- [stdlib] Cleanup polynomial.mojo, improve docstring

- [stdlib] fix math.gcd for negative numbers

- [stdlib] Simplify several methods in the UnsafePointer struct

- [stdlib] Modernise Span.__[eq/ne]__ with latest conditional conformance support

- [stdlib] Move to EqualityComparableCollectionElement

rebind

iota

polynomial.mojo

math.gcd

UnsafePointer

Span.__[eq/ne]__

EqualityComparableCollectionElement

- [stdlib] Rename _get_reference_unsafe to unsafe_get in InlineArray

- [stdlib] Move find() from StringRef to StringSlice

- [stdlib] Add a few explicit copy constructors

- [stdlib] Abort on list resize to bigger sizes without fill value

_get_reference_unsafe

unsafe_get

InlineArray

find()

StringRef

StringSlice

- [stdlib] Remove obsolete FIXME

- [stdlib] Extend tests for SIMD.__pow__

- [stdlib] Remove _div_ceil_positive in favour of math.ceildiv

- [stdlib] Remove duplicate test from PR #3279

- [stdlib] Allow !r conversion flag in String.format

- [stdlib] Fix atol parsing for prefixed integer literals with leading underscores

- [stdlib] __contains__ for ListLiteral

FIXME

SIMD.__pow__

_div_ceil_positive

math.ceildiv

!r

String.format

__contains__

- [stdlib] Add string justify methods

- [docs] Add str() in print_many() in Functions

str()

print_many()

- [stdlib] UTests for SIMD's operators required by Comparableâ

- [stdlib] UTests for FloatLiteral's operators required by Comparable

SIMD

Comparable

FloatLiteral

Comparable

- jiex-liu[stdlib] Implement remaining int fns for bit module

- [stdlib] Implement remaining int fns for bit module

- codingonion[examples] Fix typo in examples/matmul.mojo[stdlib] Fix typo in intrinsics.mojo[stdlib] Fix typo in simd.mojo

- [examples] Fix typo in examples/matmul.mojo

- [stdlib] Fix typo in intrinsics.mojo

- [stdlib] Fix typo in simd.mojo

- msaelices[stdlib] Implement Dict.setdefault(key, default)[stdlib] Implement collections.Counter - Part 2[stdlib] Disallow implicit float to int conversions in SIMD types

- [stdlib] Implement Dict.setdefault(key, default)

- [stdlib] Implement collections.Counter - Part 2

- [stdlib] Disallow implicit float to int conversions in SIMD types

- yinonburgansky[stdlib] write_to(writer, ...) -> writer.write(...)

- [stdlib] write_to(writer, ...) -> writer.write(...)

- kszucs[stdlib] Support creating nested python objects using list literals[stdlib] Enable equality comparisons for List[T] == List[T]

- [stdlib] Support creating nested python objects using list literals

- [stdlib] Enable equality comparisons for List[T] == List[T]

- martinvuyk[stdlib] Fix String.splitlines and test and move to string_slice[stdlib] Add Dict.get(key) -> Optional[T] and Dict.get(key, default) -> T methods[stdlib] Add is_valid_utf8 and test[stdlib] Fix String.split() and start fixing String.__len__()[stdlib] Add SIMD.__contains__[stdlib] Move String.isspace and _StringIter implementations to StringSlice

- [stdlib] Fix String.splitlines and test and move to string_slice

- [stdlib] Add Dict.get(key) -> Optional[T] and Dict.get(key, default) -> T methods

- [stdlib] Add is_valid_utf8 and test

- [stdlib] Fix String.split() and start fixing String.__len__()

- [stdlib] Add SIMD.__contains__

- [stdlib] Move String.isspace and _StringIter implementations to StringSlice

- haifeng-jin[GitHub Actions] Extract install build tools into separate scripts

- [GitHub Actions] Extract install build tools into separate scripts

- rd4com[Stdlib] Add Dict.__init__ overload with power_of_two_initial_capacity[Stdlib] Speedup Dict in _maybe_resize

- [Stdlib] Add Dict.__init__ overload with power_of_two_initial_capacity

- [Stdlib] Speedup Dict in _maybe_resize

- YichengDWu[stdlib] Remove incorrect alignment constraint

- [stdlib] Remove incorrect alignment constraint

- jon-chuang[examples] Improve examples/matmul.mojo by 2-3x via split-K sub-tiling

- [examples] Improve examples/matmul.mojo by 2-3x via split-K sub-tiling

- bgreni[stdlib] Make object Representable and Formattable

- [stdlib] Make object Representable and Formattable

- MatveyF[stdlib] Int comparison operator unit tests

- [stdlib] Int comparison operator unit tests

- taylorpool[Documentation] Fix typo in arc.mojo

- [Documentation] Fix typo in arc.mojo

- [stdlib] Implement remaining int fns for bit module

bit

- [examples] Fix typo in examples/matmul.mojo

- [stdlib] Fix typo in intrinsics.mojo

- [stdlib] Fix typo in simd.mojo

- [stdlib] Implement Dict.setdefault(key, default)

- [stdlib] Implement collections.Counter - Part 2

- [stdlib] Disallow implicit float to int conversions in SIMD types

Dict.setdefault(key, default)

collections.Counter

- [stdlib] write_to(writer, ...) -> writer.write(...)

write_to(writer, ...)

writer.write(...)

- [stdlib] Support creating nested python objects using list literals

- [stdlib] Enable equality comparisons for List[T] == List[T]

List[T] == List[T]

- [stdlib] Fix String.splitlines and test and move to string_slice

- [stdlib] Add Dict.get(key) -> Optional[T] and Dict.get(key, default) -> T methods

- [stdlib] Add is_valid_utf8 and test

- [stdlib] Fix String.split() and start fixing String.__len__()

- [stdlib] Add SIMD.__contains__

- [stdlib] Move String.isspace and _StringIter implementations to StringSlice

String.splitlines

Dict.get(key) -> Optional[T]

Dict.get(key, default) -> T

String.split()

String.__len__()

SIMD.__contains__

String.isspace

_StringIter

StringSlice

- [GitHub Actions] Extract install build tools into separate scripts

- [Stdlib] Add Dict.__init__ overload with power_of_two_initial_capacity

- [Stdlib] Speedup Dict in _maybe_resize

Dict.__init__

power_of_two_initial_capacity

Dict

_maybe_resize

- [stdlib] Remove incorrect alignment constraint

- [examples] Improve examples/matmul.mojo by 2-3x via split-K sub-tiling

examples/matmul.mojo

- [stdlib] Make object Representable and Formattable

object

Representable

Formattable

- [stdlib] Int comparison operator unit tests

Int

- [Documentation] Fix typo in arc.mojo

## Sign up for our newsletter, Modverse

Get all our latest news, announcements and updates delivered directly to your inbox. Unsubscribe at anytime.

Thank you for your submission.

Your report has been received and is being reviewed by the Sales team. A member from our team will reach out to you shortly.

Thank you,

Modular Sales Team

## Next blog post:

- Modverse Weekly - Issue 41August 16, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

Modverse Weekly - Issue 41

August 16, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Modverse Issues

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Jack Clayton

AI Developer Advocate

Jack started his career optimizing autonomous truck software for leading mining companies, including BHP and Caterpillar. Most recently he was designing computer vision software, putting AI inference pipelines into production for IDVerse. He is enormously passionate about the developer community, having been a Rust, Go, Python and C++ developer for over a decade. Jack enjoys making complicated topics simple and fun to learn, and heâs dedicated to teaching the world about Mojo ð¥.



================================================================================

URL: https://www.modular.com/modverse/modverse-weekly-issue-39

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

July 9, 2024

# Modverse Weekly - Issue 39

Jack Clayton

â

Check out the new section for nightly updates. If you're not keeping track on the #nightly Discord channel, you can catch up on everything new for the last week in this newsletter.

### Blogs, Tutorials, and Videos

- Read a Brief Guide to the Mojo N-Body Example and learn about orbital dynamics, along with Mojo.

- rd4com released a tutorial for the SIMDÂ type on its methods and operators.

- Santiago released a video on running Python code from Mojo.

- Alex published a blog:Â Reinforcing Learning: Using Mojoð¥ to learn 3x faster

### Awesome Mojo

You can discuss these projects with the creators in the Discord forum:Â #community

- The team working on NuMojo added an NDArray type.

- Andres released raylib bindings.â

- Frank released zstd bindings and lz4 bindings.â

- mist, weave, and mog for building TUIs moved to using integers for colors instead of strings, resulting in a 90% performance improvement.

- Shiva released a Mojo package manager:Â Tenka

### Open Source Contributions

Check out all the open source contributions here. Â make sure to DM Jack Clayton on Discord if you've had your first PRÂ merged to claim some epic Mojo swag!

Check out the merged contributions this week from our valuable community members:

- @mzaks[stdlib] benchmark sort scalar list

- [stdlib] benchmark sort scalar list

- @gabrieldemarmiesse[stdlib] Be explicit about PythonObject conversions[stdlib] Clarify error message in test_tempfile.mojo[stdlib] Add CollectionElementNew to structs (many PRs)ââ

- [stdlib] Be explicit about PythonObject conversions

- [stdlib] Clarify error message in test_tempfile.mojo

- [stdlib] Add CollectionElementNew to structs (many PRs)ââ

- @msaelicesâ[stdlib] Implement collections.Counterâ

- [stdlib] Implement collections.Counterâ

- @Brian-M-Jâ[docs] Fix typos in pointers.ipynbâ

- [docs] Fix typos in pointers.ipynbâ

- @lsh[stdlib] Add fill method to Spanâ

- [stdlib] Add fill method to Spanâ

- @bgreniâ[stdlib] Make UInt Formattableâ

- [stdlib] Make UInt Formattableâ

- @martinvuykâ[stdlib] Add SIMD.__contains__â

- [stdlib] Add SIMD.__contains__â

- @helenexâ[stdlib] Fix String.removesuffix("") removing entire string

- [stdlib] Fix String.removesuffix("") removing entire string

- [stdlib] benchmark sort scalar list

- [stdlib] Be explicit about PythonObject conversions

- [stdlib] Clarify error message in test_tempfile.mojo

- [stdlib] Add CollectionElementNew to structs (many PRs)ââ

PythonObject

test_tempfile.mojo

CollectionElementNew

â

- [stdlib] Implement collections.Counterâ

collections.Counter

- [docs] Fix typos in pointers.ipynbâ

- [stdlib] Add fill method to Spanâ

fill

Span

- [stdlib] Make UInt Formattableâ

UInt

Formattable

- [stdlib] Add SIMD.__contains__â

SIMD.__contains__

â

- [stdlib] Fix String.removesuffix("") removing entire string

String.removesuffix("")

### Nightly Updates

Check out the unreleased changelog here for more details on each change. You can follow changes on the #nightly Discord channel. These are the updates since last week:

- [Stdlib] Introduce time.perf_counter methods and deprecate time.now

- [mojo] Add an exclusive parameter to the pointer types

- [External] [stdlib] Implement collections.Counter (#42560)

- [mojo-stdlib] Adds os.path.makedirs and os.path.removedirs for adding and removing nested directories. Follows Python logic.

- [mojo-stdlib] Add os.path.split and tests, behaves the same as Python equivalent.

- [mojo-stdlib] Adds os.path.expanduser, pathlib.Path.expanduser, and pathlib.Path.home.

- [stdlib] Add math.align_{up,down} overloads for UInt

- [stdlib] Add min and max overload for UInt

- [stdlib] Update changelog with new UInt type

- [mojo-stdlib] Reland again: Add ImplicitlyBoolable trait

- [stdlib] Clean up uses of memcmp

- [mojo-lang] Rework setitem/setattr emission to use keyword arguments.

- [mojo-lang] Improve parameter inference for conditional conformances.

- [mojo-stdlib] Fallback for home dir if HOME is not set on POSIX

- [mojo-stdlib] Add the ability to expand a username with os.path.expanduser(~user/folder)

- [stdlib] Remove load from LegacyPointer.

- [mojo-stdlib] Adds the pwd module following Python syntax and behavior.

- [mojo] Update changelog with new Mojo pointer restrictions

exclusive

collections.Counter

os.path.makedirs

os.path.removedirs

os.path.split

os.path.expanduser

pathlib.Path.expanduser

pathlib.Path.home

math.align_{up,down}

UInt

min

max

UInt

UInt

ImplicitlyBoolable

memcmp

HOME

os.path.expanduser(~user/folder)

load

LegacyPointer

pwd

## Sign up for our newsletter, Modverse

Get all our latest news, announcements and updates delivered directly to your inbox. Unsubscribe at anytime.

Thank you for your submission.

Your report has been received and is being reviewed by the Sales team. A member from our team will reach out to you shortly.

Thank you,

Modular Sales Team

## Next blog post:

- Modverse Weekly - Issue 41August 16, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

Modverse Weekly - Issue 41

August 16, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Modverse Issues

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Jack Clayton

AI Developer Advocate

Jack started his career optimizing autonomous truck software for leading mining companies, including BHP and Caterpillar. Most recently he was designing computer vision software, putting AI inference pipelines into production for IDVerse. He is enormously passionate about the developer community, having been a Rust, Go, Python and C++ developer for over a decade. Jack enjoys making complicated topics simple and fun to learn, and heâs dedicated to teaching the world about Mojo ð¥.



================================================================================

URL: https://www.modular.com/modverse/modverse-weekly-38

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

June 28, 2024

# Modverse Weekly - Issue 38

Jack Clayton

The 24.4 release introduced GGUFÂ support and quantisation for models such as Llama3 and many new features for Mojo. With that behind us Modular is now working hard towards the next release. To try out the new features press the Nightly tab on the MAXÂ installation guide. There's an active community in the #nightly Discord channel to discuss the upcoming features with, and Modular staff are there to answer questions and respond to your feedback!

The latest MAXÂ nightly release now has a interactive GUIÂ chat interface for Llama3, the commands to beta test it before the official release are here.

### Blogs, Tutorials, and Videos

- Read about What's New in MAX 24.4.

- Chris Lattner talked about the broader vision for Mojo and MAX at the AI Engineer Worldâs Fair 2024.

- Billy and Walter talked about Mojo debugging: extending MLIR and LLDB.ââ

- Weiwei dived deep into Efficient Data-Flow Analysis on Region-Based Control Flow in MLIR.ââ

- Medhi and Jeff explored how MLIR can be slow generating LLVMÂ IR, and how Mojo avoids the footguns.

- Santiago released an introduction to Mojo (for Python developers)â

- Mike released a video: Mojo First Impression.

### Awesome Mojo

You can discuss these projects with the creators on the Discord channel:Â #community

- Frank built libjpeg-mojo: bindings to libjpeg from Mojo.

- Samay reinvigorated and updated arrow.mojo for the latest Mojo release.

- Kevin released mojonet: PyTorch Neural Network wrapper to help transition existing projects.

### Open Source Contributions

Check out all the open source contributions here. Any significant changes that made it into the changelog are here. make sure to DM Jack Clayton on Discord if you've had your first PRÂ merged to claim some epic Mojo swag!

Check out the merged contributions this week from our valuable community members:

- @lsh[stdlib] Add equality methods to StringSlice

- [stdlib] Add equality methods to StringSlice

- @soraros[stdlib] Move tuple.mojo off unroll[stdlib] Remove use of StaticTuple from test_simd.mojo

- [stdlib] Move tuple.mojo off unroll

- [stdlib] Remove use of StaticTuple from test_simd.mojo

- @codingonion[stdlib] Fix typo in simd.mojo[docs] Fix typo in parameters/index.ipynb

- [stdlib] Fix typo in simd.mojo

- [docs] Fix typo in parameters/index.ipynb

- @mikowals[stdlib] List __getitem__ returns auto-dereferenced ref

- [stdlib] List __getitem__ returns auto-dereferenced ref

- @gabrieldemarmiesse[stdlib] Use ValueDestructorRecorder in test_inline_list.mojo

- [stdlib] Use ValueDestructorRecorder in test_inline_list.mojo

- [stdlib] Add equality methods to StringSlice

- [stdlib] Move tuple.mojo off unroll

- [stdlib] Remove use of StaticTuple from test_simd.mojo

tuple.mojo

unroll

StaticTuple

test_simd.mojo

- [stdlib] Fix typo in simd.mojo

- [docs] Fix typo in parameters/index.ipynb

- [stdlib] List __getitem__ returns auto-dereferenced ref

- [stdlib] Use ValueDestructorRecorder in test_inline_list.mojo

ValueDestructorRecorder

test_inline_list.mojo

## Sign up for our newsletter, Modverse

Get all our latest news, announcements and updates delivered directly to your inbox. Unsubscribe at anytime.

Thank you for your submission.

Your report has been received and is being reviewed by the Sales team. A member from our team will reach out to you shortly.

Thank you,

Modular Sales Team

## Next blog post:

- Modverse Weekly - Issue 41August 16, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

Modverse Weekly - Issue 41

August 16, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Modverse Issues

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Jack Clayton

AI Developer Advocate

Jack started his career optimizing autonomous truck software for leading mining companies, including BHP and Caterpillar. Most recently he was designing computer vision software, putting AI inference pipelines into production for IDVerse. He is enormously passionate about the developer community, having been a Rust, Go, Python and C++ developer for over a decade. Jack enjoys making complicated topics simple and fun to learn, and heâs dedicated to teaching the world about Mojo ð¥.



================================================================================

URL: https://www.modular.com/modverse/modverse-weekly-37

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

June 20, 2024

# Modverse Weekly - Issue 37

Jack Clayton

We hosted the third Mojo Community Meeting this week, with great demonstrations from the community on constraints and the Mojo HTTP serving library lightbug. We also previewed our upcoming AIÂ pipeline serving feature with llama3, using a GUI to interact with it. And ended by answering community questions on async, and when Python will be able to call Mojo. This is your chance to interact with the core Mojo team, you can add it to your calendar and find the agenda for the next one here: modul.ar/community-meeting.

In other news, in less than 18 months after launching Mojo it's in the top 100 programming languages on the TIOBE Index.

### Blogs, Tutorials, and Videos

- Check out the Modular Community Livestream - New in MAX 24.4.

- Read the blog:Â Whatâs New in Mojo 24.4?

### Awesome Mojo

You can discuss these projects with the creators on the Discord channel:Â #community

- rd4com built mojo_dev_helper: a dev tool for standard library contributors.

- Ethan built random123:Â splittable pseudorandom number generators.

- Zac built mojo-json: a simple Little JSONÂ Parser.

- Mkhail has added features to gojo: UDP/TCP client/server implementations and examples

- Martin built kamo: a Kolmogorov-Arnold Networks implementation.

- ShivaSankar built HEPJo: High Energy Physics computing.

### Open Source Contributions

Check out all the open source contributions here. Any significant changes that made it into the changelog are here. make sure to DM Jack Clayton on Discord if you've had your first PRÂ merged to claim some epic Mojo swag!

## Sign up for our newsletter, Modverse

Get all our latest news, announcements and updates delivered directly to your inbox. Unsubscribe at anytime.

Thank you for your submission.

Your report has been received and is being reviewed by the Sales team. A member from our team will reach out to you shortly.

Thank you,

Modular Sales Team

## Next blog post:

- Modverse Weekly - Issue 41August 16, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

Modverse Weekly - Issue 41

August 16, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Modverse Issues

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Jack Clayton

AI Developer Advocate

Jack started his career optimizing autonomous truck software for leading mining companies, including BHP and Caterpillar. Most recently he was designing computer vision software, putting AI inference pipelines into production for IDVerse. He is enormously passionate about the developer community, having been a Rust, Go, Python and C++ developer for over a decade. Jack enjoys making complicated topics simple and fun to learn, and heâs dedicated to teaching the world about Mojo ð¥.



================================================================================

URL: https://www.modular.com/modverse/modverse-weekly-36

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

June 7, 2024

# Modverse Weekly - Issue 36

Jack Clayton

This week we released Mojoð¥ and MAXâ¡ï¸Â 24.4, there is too much in the release to cover here! Check out the launch blog here, the Mojo changelog here, and the MAXÂ changelog here.

We held another community meeting, with great presentations from community members on their exciting projects. You can watch the full video here, if you want to get involved with the next one make sure to add it to your calendar here.

### Blogs, Tutorials, and Videos

- Learn about how to speed up K-Means clustering by porting Python implementation to Mojoð¥.

- Find out how to get started with MAX release and nightly builds.

- Read up on part I:Â what ownership is really about: a mental model approach

- Go deeper with part II:Â a deep dive into ownership in Mojo

- maicmi wrote a blog on how to Install Mojo on Mac M1 and use python integration with no crash.

- Santiago shares his thoughts on Mojo while rewriting binary search functions from Python

- Numeryst released a video:Â How to Set Up Mojo with One Click?

### Awesome Mojo

- Jake built a Mojo math library for complex numbers with SIMDÂ support.

- Maxim created a Quick and Dirty JSON parser in Mojo

- Martin released a new project:Â Kolmogorov-Arnold Networks in Mojo

- Ethan built Yoho ð¥: A toy compiler written in mojo

- Ryan released Fire Physics Engine: a simple 2d physics engine in Mojo

- Ryulord created awdy: a tqdm-like progress bar for Mojo

- Martin built mopro: a basic progress bar for Mojo

- Benny released a wrapper around GLibC for use in Mojo programs

- Benny also built MIP:Â a work in progress image library for Mojo, inspired by PIL

### Open Source Contributions

Check out all the open source contributions here. Any significant changes that made it into the changelog are here. make sure to DM Jack Clayton on Discord if you've had your first PRÂ merged to claim some epic Mojo swag!

## Sign up for our newsletter, Modverse

Get all our latest news, announcements and updates delivered directly to your inbox. Unsubscribe at anytime.

Thank you for your submission.

Your report has been received and is being reviewed by the Sales team. A member from our team will reach out to you shortly.

Thank you,

Modular Sales Team

## Next blog post:

- Modverse Weekly - Issue 41August 16, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

Modverse Weekly - Issue 41

August 16, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Modverse Issues

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Jack Clayton

AI Developer Advocate

Jack started his career optimizing autonomous truck software for leading mining companies, including BHP and Caterpillar. Most recently he was designing computer vision software, putting AI inference pipelines into production for IDVerse. He is enormously passionate about the developer community, having been a Rust, Go, Python and C++ developer for over a decade. Jack enjoys making complicated topics simple and fun to learn, and heâs dedicated to teaching the world about Mojo ð¥.



================================================================================

URL: https://www.modular.com/modverse/modverse-weekly-35

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

May 23, 2024

# Modverse Weekly - Issue 35

Jack Clayton

This week we held the first ever Mojo community meeting! Many topics were discussed, including priorities, diving into specific features like async, and topics set on the agenda by the wider Mojo community. Next week we'll be hearing from community members about the awesome projects they've been working on. Make sure to add the next community meeting to your calendar.

### Blogs, Tutorials, and Videos

- geauxeric created a video tutorial: Implement and benchmark softmax algorithms in Mojo

- Learn how to implement Fastâ¡ï¸ k-means clustering in Mojoð¥

- Check out this unofficial Chinese community Mojo documentation and forum

### Awesome Mojo

- Aaron created a build system for Mojo using Nix and Bazel: rules_mojo

- Benny created a Mojo wrapper around PILÂ (Python Imaging Library) named MIL

- Sebastian rewrote the Python heapq module in Mojo: heapq.mojo

### Open Source Contributions

Check out all the open source contributions here. Any significant changes that made it into the changelog are here. make sure to DM Jack Clayton on Discord if you've had your first PRÂ merged to claim some epic Mojo swag!

## Sign up for our newsletter, Modverse

Get all our latest news, announcements and updates delivered directly to your inbox. Unsubscribe at anytime.

Thank you for your submission.

Your report has been received and is being reviewed by the Sales team. A member from our team will reach out to you shortly.

Thank you,

Modular Sales Team

## Next blog post:

- Modverse Weekly - Issue 41August 16, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

Modverse Weekly - Issue 41

August 16, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Modverse Issues

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Jack Clayton

AI Developer Advocate

Jack started his career optimizing autonomous truck software for leading mining companies, including BHP and Caterpillar. Most recently he was designing computer vision software, putting AI inference pipelines into production for IDVerse. He is enormously passionate about the developer community, having been a Rust, Go, Python and C++ developer for over a decade. Jack enjoys making complicated topics simple and fun to learn, and heâs dedicated to teaching the world about Mojo ð¥.



================================================================================

URL: https://www.modular.com/modverse/modverse-weekly-34

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

May 17, 2024

# Modverse Weekly - Issue 34

Jack Clayton

This week we launched MAXâ¡ï¸ nightlies with preview support for macOS and MAXÂ Serving! You can now find details about how to install it on modul.ar/get-started, press the Nightly button on the install guide if you want to be on the bleeding edge. We look forward to your feedback, join the Discord and leave comments on the #nightly channel.

The #community-blogs-videos and #community-projects channels on Discord have been archived in favor of a #community forum. This allows users to discuss your projects and content, and keep each item discoverable for ongoing discussions.

### Blogs, Tutorials, and Videos

- ivellapillil created a very thorough online book on Mojo with helpful diagrams and graphics!

- Read a tutorial on the MAX graph API

- Watch a deep dive tech talk from Chris Lattner on ownership and lifetimes in Mojo

- Check out an Introduction to MAX Graph API and custom operators

- Read Joe Pamer's view on the future direction of Mojo

- âGabriel linked his tutorial on making a pull request to an open-source project

### Awesome Mojo

- Martin released an efficient string builder named mostring

- rd4com released a Mojo test runner

### Open Source Contributions

Check out all the open source contributions here. Any significant changes that made it into the changelog are here. make sure to DM Jack Clayton on Discord if you've had your first PRÂ merged to claim some epic Mojo swag!

## Sign up for our newsletter, Modverse

Get all our latest news, announcements and updates delivered directly to your inbox. Unsubscribe at anytime.

Thank you for your submission.

Your report has been received and is being reviewed by the Sales team. A member from our team will reach out to you shortly.

Thank you,

Modular Sales Team

## Next blog post:

- Modverse Weekly - Issue 41August 16, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

Modverse Weekly - Issue 41

August 16, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Modverse Issues

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Jack Clayton

AI Developer Advocate

Jack started his career optimizing autonomous truck software for leading mining companies, including BHP and Caterpillar. Most recently he was designing computer vision software, putting AI inference pipelines into production for IDVerse. He is enormously passionate about the developer community, having been a Rust, Go, Python and C++ developer for over a decade. Jack enjoys making complicated topics simple and fun to learn, and heâs dedicated to teaching the world about Mojo ð¥.



================================================================================

URL: https://www.modular.com/modverse/modverse-weekly-33

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

May 10, 2024

# Modverse Weekly - Issue 33

Jack Clayton

This week we hosted a livestream with many good questions from the community. People were most interested in macOS support for MAX, and GPUÂ support. These are both top priority at Modular so stay tuned! The standard library team were happy to announce that we've had 250 OSS contributions since open sourcing, we continue to be impressed and motivated by our active community!

Also check out the new proposal on automatic deref in Mojo. This is to solve the need to use [] to deref, for example when iterating through a list. The overwhelming consensus from the community is in support of the "eager reference decay" implementation, which will be familiar to C++ developers, and also provide a much better experience for developers transitioning from Python.

### Blogs, Tutorials, and Videos

- Ferdinand wrote a detailed blog on parsing PNGÂ images with Mojo and released a library named mimage for reading images.

- Watch our livestream on MAX and Mojo 24.3 with cool examples like using the graph API with custom ops, and running Yolo and Stable Diffusion through MAX.

- Read the summarized version of Chris' epic Mojo deep dive on the Developer Voices podcast.

### Awesome Mojo

- Martin built another Andrei Karparthy port: minbpe used for LLM tokenization.

- Community collaboration MLÂ Framework Basalt has had a large number of updates including many new ops, import / export to ONNX, and dynamic ops.

- rd4com built a web framework on top of Lukas' lsx HTMLÂ building library.

- Maxim updated his mojo-sort library to work with the latest nightly compiler, along with performance improvements.

- Dimitri released a library named toybox with a disjoint set.

### Open Source Contributions

The open source contributions each week have become too large to list! We merged 44 since the last newsletter, you can find them all here. Any significant changes that made it into the changelog are here. Note that we've moved to using a tool named Copybara to keep the external and internal repo in sync. This automation has allowed us to push out nightly compiler releases at a much more regular cadence. Thanks for all the great contributions, make sure to DM Jack Clayton on Discord if you've had a PRÂ merged to claim some epic Mojo swag!

## Sign up for our newsletter, Modverse

Get all our latest news, announcements and updates delivered directly to your inbox. Unsubscribe at anytime.

Thank you for your submission.

Your report has been received and is being reviewed by the Sales team. A member from our team will reach out to you shortly.

Thank you,

Modular Sales Team

## Next blog post:

- Modverse Weekly - Issue 41August 16, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

Modverse Weekly - Issue 41

August 16, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Modverse Issues

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Jack Clayton

AI Developer Advocate

Jack started his career optimizing autonomous truck software for leading mining companies, including BHP and Caterpillar. Most recently he was designing computer vision software, putting AI inference pipelines into production for IDVerse. He is enormously passionate about the developer community, having been a Rust, Go, Python and C++ developer for over a decade. Jack enjoys making complicated topics simple and fun to learn, and heâs dedicated to teaching the world about Mojo ð¥.



================================================================================

URL: https://www.modular.com/modverse/modverse-weekly-32

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

May 3, 2024

# Modverse Weekly - Issue 32

Jack Clayton

Yesterday marked Mojoð¥'s first birthday ð, launching last year on May 2, 2023. We launched with just an online Jupyter environment and have since released the SDK locally for macOS and Linux on arm64 and x86, and bought up our inference and serving stack MAXâ¡ï¸. Yesterday we released Mojo and MAX 24.3 to mark the occasion, along with the first community contributions, 32 of which made it into the official changelog. We're so excited to go on this journey out in the open with our amazing community, thank you to everyone who has been participating!

### Blogs, Tutorials, and Videos

- Read our blog on whatâs New in Mojo 24.3.

- Learn about a new feature in MAX 24.3: Engine Extensibility.

- Watch an in-depth interview about Mojo and MAX from Kris Jenkins Developer Voices podcast with Chris Lattner.

- Read an X tutorial for bit operations on SIMD values and integers.

- Check out our blog on multimodal search with Snowflake embedding and MAX Engine.

- Learn about how we prevent supply chain attacks at Modular.

- Chris talked about programming and AI at Startup Grind.

- RD4com created a tutorial on parameterizing and compile time constraining a Mojo app.

### Awesome Mojo

- Vladyslav updated mojo-atol-simd and it's now up to 20% faster than the stdlib.

- Benny added flag features to his Mojo CLI library prism.

- Alex updated mojo-pytest with support for the latest 24.3 release.

- MadAlex has continued development on his numpy-like Mojo library with up to 6x better performance: NuMojo.

### MAXÂ Engine

- ONNX20% speedup for llama2 & mistral-7b20% speedup on stable-diffusion25-45% speedups on image encoders25-45% speedups on dynamically-quantized transformers

- 20% speedup for llama2 & mistral-7b

- 20% speedup on stable-diffusion

- 25-45% speedups on image encoders

- 25-45% speedups on dynamically-quantized transformers

- PyTorch20% speedups for mistral-7b20% speedup on llama225% speedup on dlrm-rm2-multihot25% speedup on replit-3b

- 20% speedups for mistral-7b

- 20% speedup on llama2

- 25% speedup on dlrm-rm2-multihot

- 25% speedup on replit-3b

- 20% speedup for llama2 & mistral-7b

- 20% speedup on stable-diffusion

- 25-45% speedups on image encoders

- 25-45% speedups on dynamically-quantized transformers

- 20% speedups for mistral-7b

- 20% speedup on llama2

- 25% speedup on dlrm-rm2-multihot

- 25% speedup on replit-3b

### Open Source Contributions

- Gabriel had 10 PRs merged!#2454: Allow StringRef to work with both Int8 and UInt8#2451:Â Removed FileCheck from the dev guide#2448: Allow String() to be created from UInt8 ptr and List too#2407: Use UnsafePointer in builtin_list.mojo#2405: Use UnsafePointer in _cpython.mojo#2404: Use UnsafePointer in info.mojo and some test files#2403: Use UnsafePointer in hex|io|os.mojo #2402: Replace Pointer by UnsafePointer in hash|simd.mojo#2361: Add repr() function and Representable trait#2024:Â Remove FileCheck in test_object.mojo and removed object.print()

- #2454: Allow StringRef to work with both Int8 and UInt8

- #2451:Â Removed FileCheck from the dev guide

- #2448: Allow String() to be created from UInt8 ptr and List too

- #2407: Use UnsafePointer in builtin_list.mojo

- #2405: Use UnsafePointer in _cpython.mojo

- #2404: Use UnsafePointer in info.mojo and some test files

- #2403: Use UnsafePointer in hex|io|os.mojo

- #2402: Replace Pointer by UnsafePointer in hash|simd.mojo

- #2361: Add repr() function and Representable trait

- #2024:Â Remove FileCheck in test_object.mojo and removed object.print()

- #2364 mikowals:Â add b64decode

- #2315 mikowals: SIMD.shuffle with StaticIntTuple mask

- #2294 lsh: Create InlineArray type

- #2190 arvindavoudi: Add new ComparableCollectionElement trait and index(list, value, start, end) function APIs

- #2454: Allow StringRef to work with both Int8 and UInt8

- #2451:Â Removed FileCheck from the dev guide

- #2448: Allow String() to be created from UInt8 ptr and List too

- #2407: Use UnsafePointer in builtin_list.mojo

- #2405: Use UnsafePointer in _cpython.mojo

- #2404: Use UnsafePointer in info.mojo and some test files

- #2403: Use UnsafePointer in hex|io|os.mojo

- #2402: Replace Pointer by UnsafePointer in hash|simd.mojo

- #2361: Add repr() function and Representable trait

- #2024:Â Remove FileCheck in test_object.mojo and removed object.print()

â

## Sign up for our newsletter, Modverse

Get all our latest news, announcements and updates delivered directly to your inbox. Unsubscribe at anytime.

Thank you for your submission.

Your report has been received and is being reviewed by the Sales team. A member from our team will reach out to you shortly.

Thank you,

Modular Sales Team

## Next blog post:

- Modverse Weekly - Issue 41August 16, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

Modverse Weekly - Issue 41

August 16, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Modverse Issues

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Jack Clayton

AI Developer Advocate

Jack started his career optimizing autonomous truck software for leading mining companies, including BHP and Caterpillar. Most recently he was designing computer vision software, putting AI inference pipelines into production for IDVerse. He is enormously passionate about the developer community, having been a Rust, Go, Python and C++ developer for over a decade. Jack enjoys making complicated topics simple and fun to learn, and heâs dedicated to teaching the world about Mojo ð¥.



================================================================================

URL: https://www.modular.com/?205c58fe_page=2

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

80K+ developers building with MAX

# A high-performance generative AI framework

MAX replaces and simplifies your AI inference stack, maximizing performance and reducing cloud costs

## Achieve state of the art NVIDIAÂ GPU performance

Unlock state of the art latency and throughput without writing low-level CUDA code.

### Deploy MAX on GPUs

## Optimize your existing PyTorch & ONNX models

Migrate seamlessly without rewriting your AI models and pipelines on a unified AI stack.

## Use Mojo to supercharge your AIÂ applications

Extend your PythonÂ code with high-performance Mojo, a new programming language with the expressiveness of Python and the performance of C.

## Develop locally, deploy globally to any cloud.

Develop your AI applications locally and package and deploy across any cloud provider, on CPUs and GPUs, without having to change your code.

## Run Gen AI locally underÂ 3Â minutes

Download MAX first to run an LLM

- Run Llama3-8B in your CLIGet started

- Run Llama2-7B in your CLIGet started

- Run Replit-3B in your CLIGet started

Run Llama3-8B in your CLI

Run Llama2-7B in your CLI

Run Replit-3B in your CLI

## Free Forever

The MAXÂ framework is a free and open platform for you to develop and deploy AI inference workloads.

A new framework for Gen AI, and the best way to deploy PyTorch

Development tools for accelerated compute on GPUs and CPUs, built from the ground up for GenAI, but compatible with today.

## Developer Approved ð

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

## Get started now

- Getting started guideStep by step walkthrough on how to get started

- Browse examplesRun a project locally in just 2min

- Connect with buildersJoin over 22,000 developers

Getting started guide

Step by step walkthrough on how to get started

Browse examples

Run a project locally in just 2min

Connect with builders

Join over 22,000 developers

Download for your platformÂ now

- 

- 

- 

View Pricing

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

MAX on GPU waitingÂ list

Be the first to get lightning fast inference speed on your GPUs. Be the envy of all your competitors and lower your compute spend.

================================================================================

URL: https://www.modular.com/#open-roles

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

80K+ developers building with MAX

# A high-performance generative AI framework

MAX replaces and simplifies your AI inference stack, maximizing performance and reducing cloud costs

## Achieve state of the art NVIDIAÂ GPU performance

Unlock state of the art latency and throughput without writing low-level CUDA code.

### Deploy MAX on GPUs

## Optimize your existing PyTorch & ONNX models

Migrate seamlessly without rewriting your AI models and pipelines on a unified AI stack.

## Use Mojo to supercharge your AIÂ applications

Extend your PythonÂ code with high-performance Mojo, a new programming language with the expressiveness of Python and the performance of C.

## Develop locally, deploy globally to any cloud.

Develop your AI applications locally and package and deploy across any cloud provider, on CPUs and GPUs, without having to change your code.

## Run Gen AI locally underÂ 3Â minutes

Download MAX first to run an LLM

- Run Llama3-8B in your CLIGet started

- Run Llama2-7B in your CLIGet started

- Run Replit-3B in your CLIGet started

Run Llama3-8B in your CLI

Run Llama2-7B in your CLI

Run Replit-3B in your CLI

## Free Forever

The MAXÂ framework is a free and open platform for you to develop and deploy AI inference workloads.

A new framework for Gen AI, and the best way to deploy PyTorch

Development tools for accelerated compute on GPUs and CPUs, built from the ground up for GenAI, but compatible with today.

## Developer Approved ð

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

## Get started now

- Getting started guideStep by step walkthrough on how to get started

- Browse examplesRun a project locally in just 2min

- Connect with buildersJoin over 22,000 developers

Getting started guide

Step by step walkthrough on how to get started

Browse examples

Run a project locally in just 2min

Connect with builders

Join over 22,000 developers

Download for your platformÂ now

- 

- 

- 

View Pricing

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

MAX on GPU waitingÂ list

Be the first to get lightning fast inference speed on your GPUs. Be the envy of all your competitors and lower your compute spend.

================================================================================

URL: https://www.modular.com/blog/how-we-work

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

May 21, 2022

# How we work

Chris Lattner

Tim Davis

#### Modular is not a typical company, and we never intend to become one. As a team, we have collectively worked at more than 50 different companies, from the largest technology companies to some of the smallest - and weâve seen a lot of things that work well, and a lot that just donât.

#### Weâre building for the long term, and one of the most important pieces to get right is culture - it allows the team to stay healthy, stay aligned in purpose, and remain high functioning as the company scales. So why is it that so few companies pay attention to culture early on? We believe that building a truly great culture requires codifying it, living it, and entrenching it in all aspects of how you work as a team.

## A changing world

For most of us, the world significantly shifted two years ago. I recall preparing for a TensorFlow Dev Summit when we were all abruptly told that we could no longer return to the office - we were told to work from home âfor the foreseeable future.â It turned out that would be the last time I would ever go back to an office as I knew it, and the last time I would see my team physically for a long time.

Before the pandemic, you either chose where to live and found work accordingly, or you chose where to work and found living arrangements accordingly. You didnât get to mix and match. But the last two years have certainly changed the reality of what âgoing to workâ actually means, and for many of us, transformed how we balance life and work together.

At Modular, we believe in optimizing for the highest productivity and happiness for our team, not for maximum utilization of our real estate, location, or entrenched ideologies of years gone past. While some people prefer a collaborative shared space, others prefer zero-length commutes, and others prefer a hybrid. We know that âone size fits allâ solutions are âone size fits noneâ in reality, and we strongly believe that this model doesnât represent the future of work, nor does it embrace the lessons that weâve all learned the last two years.

The nature of work has forever changed.

One thing is manifestly clear - especially if you work in technology, you can now choose both where to live and where to work. You get to optimize how you spend your time to be the most productive at work, and how to maximize your life outside it. To quote the great Steve Jobs - you only have so much time and so much life:

## The culture we practice, is the culture we keep

At Modular, we get a lot of feedback from folks who are impressed that we have already invested into culture- that we have taken the time to carefully consider and define what our culture is and that we have been directive on how weâve implemented it. We did this because we have seen firsthand the impact of toxic cultures. Weâve also seen what happens when high-power people arenât aligned - pushing and pulling into conflicting directions without knowing where the North Star is. We've seen environments that reward meeting time, endless presentations and misaligned âtechnical contributionsâ over actually building something meaningful for users with a focus on shipping it.

Posters on a wall or words on a website mean nothing if people at a company donât actually use and action those words in how they work every day. Weâve seen a lot of lip service given to culture without an attempt to live it or embody the intended culture in actions. Culture must be lived and practiced everywhere. It has to be in your objectives, it has to be in your hiring, in your daily language, and in the standard you hold everyone to. Otherwise, like all things without constant practice, it fades away and becomes meaningless.

We were very deliberate about our culture when we founded Modular, from why we work, to how we work, to ensuring we aligned everything back to our core cultural pillars: Build products users love, Empower people, and Be an incredible team.

You might be wondering how we do this in practice? Here are just some examples:

### Build products users love

- Customer-led products - To build products users love, we regularly listen to our customers to deeply understand their problems. Our goal is to build incredible technology that is actually useful and that solves real problems. Modular isnât a research project.

- We write things down - This is key to enabling asynchronous, highly productive work, and it allows real-time discussions to be more powerful, thoughtful, and contextually driven. Importantly, it ensures we are scaling and leveraging our time right, so that people who join later understand why certain decisions were made.

- Use the product - We use what we build. We regularly demo, refine, and simplify it and we constantly improve the end-to-end experience along the way.

- We say no, a lot - We say no to so much more than we say yes to. The DNA we bring from Apple includes a culture of debate, a focus of what matters and a willingness to sequence deliverables. This means we arenât afraid to say ânoâ or at least ânot yetâ, even to really appealing projects.

- Retrospective Champions - Retrospectives after sprints and milestones are a constructive way to reflect on the things that went well, the things that werenât so great and how to improve. We take it a step further and allocate Retrospective Champions to specific Action Items to ensure they actually get done.

### Empower people

- Trust - We trust our people. We don't micromanage each other - we just expect stuff to get done and always assume the best intentions along the way.

- Open calendars, meetings & docs - We donât have anything to hide. We make everything we can available to everyone. There are no âbusyâ calendar's here, no secret meetings or decisions made without broad communication and input.

- Career maps, planning - We already care about mapping out the careers of our team and do so via 28, 56 and 84-day onboarding check-ins. We utilize a range of tools to make sure that everyone has a chance to grow, even as we rapidly scale.

- We work smarter - You are not expected to give up your life. People who get to burnout, often give up. Our time horizon isn't measured in days. We want you to bring the best of yourself to work, so you can truly enjoy your non-work time.

### Be an incredible team

- We hire & provide offers fast - We interview and extend offers for the best people with limited process. We view hiring as a bilateral exchange - we are interviewing you, and you are interviewing us.

- Technical management - We believe in managers being technical, and having a strong appreciation for the complexity of what is being built and driving the state of the art forward.

- Lightweight reviews - Big technology performance reviews are insanely time consuming and exhausting. As much as they try to be independent, they end up being popularity contests. We have regular and direct career discussions and lightweight promotion decisions that tie ratings back to our values.

- We celebrate everything - We celebrate our accomplishments and failures every week - professional or personal. You canât be an incredible team, if you donât win and fail together.

And this is just a few of the things we do - we are continuing to push ourselves each day to improve.

## Maximizing how we work

As we continue to grow and innovate, we are constantly improving and changing. We will always push the limits to create the best possible environment for our people and teams. Today, we are sharing our philosophy on how we empower our people to be their best. We will always push the limit to enable even more choice and flexibility because we believe that when the right people are empowered the right way, we accelerate our impact on the world.

Every single person who works at Modular can:

- Work from anywhere in your country - You are able to work from anywhere we can support, e.g. at home or in a co-location office - whatever suits you.

- Compensation that doesnât depend on which city you live in - Start in SF, move to New Orleans? Go for it - your compensation will not change. We will always pay top industry rates, no matter where you are in your country.

- Synchronous & asynchronous days - Collaborate, brainstorm, and meet with your team when you need, and work asynchronously on days where you want to get stuff done. We usually do 2-3 synchronous days a week and the rest are asynchronous.

- Regular on-sites, local meetups - We regularly meet as a company in different US locations (i.e. about a week a quarter), and people clustered together can also host local fun event meetups every couple of weeks in their respective cities. This is in addition to co-location work days. We also require that every company-wide onsite is in a different state to avoid one geolocation becoming the âdefacto HQâ - it also makes life a lot more fun, visiting different places.

- Travel & work internationally - We support employees who want to visit family, live abroad for a period, or for other personal reasons, up to the legal limits. We want to support you and know productivity isn't tied to a zip code.

We do not believe that innovation is âtrapped in a room with a whiteboard.â Companies are just collections of people - they succeed or fail by how well they can work together, listen and respond to their customers, build and ship products to people who want them, and keep pushing the path of innovation forward. We believe that by empowering the best in the world, who believe in our culture and values - we can innovate, build, and ship better products, faster.

## Join us

In our view, to truly build the future, you need to live and work in it. The future is built by responding and adapting to constant change - where people can do their best work, and live their best life. We think the future of AI infrastructure will be built by those who believe in our new and ever changing world, and who are based all over it.

We are building a legendary team; the absolute best in the industry with a bold vision for the future. If this resonates with you, and if you want to help us enable AI anywhere, for anyone, we encourage you to join the best AI infrastructure team in the world today.

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Culture

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Chris Lattner

Co-Founder & CEO

Distinguished Leader who founded and scaled critical infrastructure including LLVM, Clang, MLIR, Cloud TPUs and the Swift programming language. Chris built AI and core systems at multiple world leading technology companies including Apple, Google, SiFive and Tesla.

clattner@modular.com

Tim Davis

Co-Founder & President

Repeat Entrepreneur and Product Leader. Tim helped build, found and scale large parts of Google's AI infrastructure at Google Brain and Core Systems from APIs (TensorFlow), Compilers (XLA & MLIR) and runtimes for server (CPU/GPU/TPU) and TF Lite (Mobile/Micro/Web), Android ML & NNAPI, large model infrastructure & OSS for billions of users and devices. Loves running, building and scaling products to help people, and the world.

tim@modular.com

================================================================================

URL: https://www.modular.com/culture

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# A great culture is the key to creating a great company.

## What we believe

### Build products usersÂ love â¤ï¸

Customers first

We build technology to lift the world by solving our customers' problems. We understand our users' problems and use cases and know there is sometimes a trade-off between "what is important" and "what is interesting" when building technology. We are clear that our customers always come first, and we always deliver on our promises.

Build it right

We build high-quality production software that displays technical mastery inside and out. We make our infrastructure the right way because we understand how quickly technical debt compounds. We know how faster engineering, product development, and business metrics move with a solid foundation. We build scalable, modular, and reliable systems that meet our customers' needs.

Drive results

When our customers and their people succeed, our company succeeds. No matter their role, every employee contributes to our collective success. Individually and together, we are all aligned on what we are working towards and how we can contribute. But we don't just work on auto-pilot. We expect everyone to regularly step back and ask if we're measuring the right input and output. We all win when we drive results that actually matter.

### Empower people ðª

Ownership

We act on behalf of the entire organization and not just for ourselves and our local team. We operate on the assumption that each person is highly motivated and will drive their work with a bias towards action. We drive ownership by tracking progress toward our goals with clear expectations, responsible owners, and solid execution. We expect everyone to act like an owner, and once a decision is made, everyone will fully commit and move forward together.

Transparency

We are transparent by having decisions, calendars, directions, and plans open to everyone in the company. Anyone can constructively ask tough questions and feel safe to have answers provided directly. We expect people to be clear about what they know and open about what they donât. We want everyone to feel that they can be vulnerable in front of each other and feel safe to take risks because this is how we grow and innovate.

Hire the best, never stop learning

We hire great people who seek to constantly grow themselves and others around them. Great people donât settle; they have a thirst for knowledge and new skills, a desire for self-improvement, and actively seek feedback whenever they can. Great people develop great people; they take coaching others seriously and look to raise the standards of the whole organization.

Have fun, live life

We believe people do their best work when they are happy. Our goal is to ensure that you will always have the environment to achieve the right mix of family, compensation, growth, and mission to live a balanced and fulfilling life. By having fun and living your life, you also see more of the world, appreciate it, and learn how we can help improve it.

### Be an incredible teamÂ ð¤

Win together, fail together

Changing the world is a team sport, and we need a group of incredibly talented people to be successful. Individual wins, or mistakes, matter less than everyone winning, failing, learning, and growing together. We expect everyone to listen, speak openly, and treat others with respect regardless of the wins or losses along the way. We foster a blameless culture for mistakes, and we expect our team to take risks without feeling insecure or embarrassed. The more we trust each other, the more we can lean on each other, and the more we can learn and grow together.

Own inclusion, be diverse

We build for everyone, and we are open to everyone. We believe that the best results come from a team that reflects the world at large. Our customers are diverse, and so are their needs, so we must empower and promote diversity and diverse perspectives at all company levels. We need to build diverse teams and foster diverse thinking to create products that genuinely help the world.

Everyone has a voice

We expect the best concept, design, plan, or direction to succeed regardless of someoneâs ârankâ in the company. Innovation exists everywhere, and great ideas are inside all of us. You can always reach out to anyone at any level in the company. We foster open communication and constructive debate and expect everyone to accept respectful challenges to their positions. We believe that the job of a leader is to find the right answer with their team, not to magically know everything themselves.

Assume positive intent

We expect everyone to assume the best of others. Our experiences have shown that information asymmetry almost always exists between people, and taking the time to understand circumstances via transparent communication is key to building healthy and functioning teams. We're all human and often jump to conclusions; we want people to assume good intentions when we do.

ð Weâre hiring!

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/#form

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

80K+ developers building with MAX

# A high-performance generative AI framework

MAX replaces and simplifies your AI inference stack, maximizing performance and reducing cloud costs

## Achieve state of the art NVIDIAÂ GPU performance

Unlock state of the art latency and throughput without writing low-level CUDA code.

### Deploy MAX on GPUs

## Optimize your existing PyTorch & ONNX models

Migrate seamlessly without rewriting your AI models and pipelines on a unified AI stack.

## Use Mojo to supercharge your AIÂ applications

Extend your PythonÂ code with high-performance Mojo, a new programming language with the expressiveness of Python and the performance of C.

## Develop locally, deploy globally to any cloud.

Develop your AI applications locally and package and deploy across any cloud provider, on CPUs and GPUs, without having to change your code.

## Run Gen AI locally underÂ 3Â minutes

Download MAX first to run an LLM

- Run Llama3-8B in your CLIGet started

- Run Llama2-7B in your CLIGet started

- Run Replit-3B in your CLIGet started

Run Llama3-8B in your CLI

Run Llama2-7B in your CLI

Run Replit-3B in your CLI

## Free Forever

The MAXÂ framework is a free and open platform for you to develop and deploy AI inference workloads.

A new framework for Gen AI, and the best way to deploy PyTorch

Development tools for accelerated compute on GPUs and CPUs, built from the ground up for GenAI, but compatible with today.

## Developer Approved ð

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

## Get started now

- Getting started guideStep by step walkthrough on how to get started

- Browse examplesRun a project locally in just 2min

- Connect with buildersJoin over 22,000 developers

Getting started guide

Step by step walkthrough on how to get started

Browse examples

Run a project locally in just 2min

Connect with builders

Join over 22,000 developers

Download for your platformÂ now

- 

- 

- 

View Pricing

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

MAX on GPU waitingÂ list

Be the first to get lightning fast inference speed on your GPUs. Be the envy of all your competitors and lower your compute spend.

================================================================================

URL: https://www.modular.com/blog/ develop-locally-deploy-globally

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

July 9, 2024

# Develop locally, deploy globally

Modular Team

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

In particular, it remains nearly impossible to build streamlined and scalable development workflows that bridge the gap between local development and cloud deployment. These workflows are complicated because AI tooling is fragmented, with each tool presenting its own trade-offs and limitations.

Todayâs AI developers must leverage a multitude of different tools across their end-to-end AI workflows. Some tools work for cloud accelerators but are limited to local CPUs, while others excel for local CPU development but arenât cloud-enabled. Working across these tools requires problematic abstractions and translators to unify fundamentally incompatible technologies, requiring rewriting of model or application code to make it all work together.

Sometimes, it seems impossible to piece together the right combination of tools and hardware to support the full developer lifecycle and provide the best performance-to-cost trade-off for any given model.

## How MAX helps

MAX solves these problems by providing a unified inference API backed by a state-of-the-art compiler and inference runtime that works with a variety of models and hardware types across local laptops and common cloud instances. MAX doesn't require you to migrate your entire AI pipeline and serving infrastructure to something newâit meets you where you are now and allows you to upgrade incrementally.

MAX makes your pipeline portable across a wide range of CPU architecturesâIntel, AMD, and ARMâand GPUs, opening up more portability and performance. MAX allows you to take advantage of the breadth and depth of different cloud instances at the best price, ensuring you always get the best inference cost-performance ratio.

### MAX unlocks local to cloud

With MAX, you can develop AI applications locally and then easily package them for deployment to any cloud environment. This unified workflow offers several critical advantages:

- Speed and Flexibility: Downloading MAX enables local development for rapid iteration and testing. Importantly, MAX doesnât just run locally but is faster on many use cases than industry standard local LLM frameworks, like llama.cpp, making development even faster. You can quickly modify code, run experiments, and debug issues without the setup and latency associated with remote servers.

- Resource Availability: On local machines, you have direct access to local hardware resources, such as CPU and GPUs, for free. Spinning up and paying for cloud instances is unnecessary and isnât bottlenecked by availability.

- Control and Customization: Local environments can be tailored to specific project needs, allowing developers to install custom libraries, frameworks, and tools without restrictions. Importantly, for latency-sensitive applications, fully controlled local development boxes provide less noise than shared cloud instances.

- Consistency: Developing with MAX locally and bundling our infrastructure into OCI-compatible containers like Docker means you can ensure that what you build locally works seamlessly in production environments.

- Global Scale: Finally, MAX is highly optimized for the AI hardware running in popular data center cloud providers like AWS, Azure, and GCPâincluding CPU and GPUs (coming soon!). Users can scale their AI applications globally with no code changes and state-of-the-art performance.

## How MAX works

The industry lacks a unified AI infrastructure platform and is instead flush with âpoint solutionsâ that only work for specific models, hardware, and OSâs. When we began the effort to unify the world's AI infrastructure, we realized fragmentation in the stack was driven by a lack of a common programming model for AI hardware. That's why we created Mojo, a new unifying programming language for AI hardware that combines Pythonâs expressiveness with Câs performance.

Mojo is the core technology that provides the foundation for the rest of the MAX platform. The MAX Engine, our next-generation graph compiler and runtime system, leverages Mojo to implement its low-level mathematical operations, such as MatMul. This provides unparalleled portability and performance and makes local-to-cloud workflows possible. You can load any model into MAX Engine and achieve low-latency inference on a wide range of hardware.

All that said, you do not need to use Mojo to use MAX. You can bring your existing models to the framework and execute them with MAX Engine using our API libraries in Python and C. However, using Mojo with MAX gives you superpowers. Mojo allows you to write custom ops for your model or write a full inference graph for optimal performance in MAX Engine.

Finally, integrating MAX as part of your AI development workflow makes it easy to deploy your locally developed models into production using trustworthy tools that include robust scaling, monitoring, and deployment templates.

With MAX, you can serve your model using industry-compatible tools, including NVIDIA's Triton Server. Because we already interoperate with the tools you already use, we make it easy to drop MAX into your existing workflows that require model versioning, multi-model deployment, and support for various deployment environments, making it a versatile solution for AI inference deployment. Read about how easy this is right now!

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Product

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Modular Team

Company

Our mission is to have real, positive impact in the world by reinventing the way AI technology is developed and deployed into production with a next-generation developer platform.



================================================================================

URL: https://www.modular.com/blog/ bring-your-own-pytorch-model

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

July 9, 2024

# Bring your own PyTorch model

Modular Team

â

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

PyTorch has become the top choice for researchers pushing the boundaries of AI, thanks to its blend of flexibility, Pythonic simplicity, and robust community support. Despite its popularity and success in research environments, PyTorch faces challenges in large-scale production deployments. What makes PyTorch excellent for developmentâlike its Python eager modeâcauses difficulties in production settings where resource management, latency targets, and reliability are critical.

While research teams favor Python for its ease of use, deployment teams often use high-performance languages and libraries like C++ and CUDA to optimize models for latency, throughput, and cost efficiency. Despite efforts to optimize the PyTorch deployment processes (e.g., TorchScript), a universal method for deploying high-performance PyTorch models at scale remains elusive.

Recently, developers have resorted to building point-solutions to deploy LLMs (that is, use-case-specific solutions such as TRT-LLM and vLLM), which further fragment the industry and increase complexity for everyone.

â

This ongoing challenge underscores the complexities inherent in bridging the gap between research-driven AI development and robust, scalable AI deployment. But it also describes one of the core reasons we built MAXâthe Modular Accelerated Xecution platform.

Simply put, MAX provides the best way to deploy PyTorch into production.

### MAX unlocks the full power of PyTorch

MAX provides inference API backed by a state-of-the-art compiler and inference runtime that works with a variety of models and hardware types, across local laptops and common cloud instances. Importantly, MAX doesn't require that you rewrite your PyTorch modelsâit meets you where you are now, using your existing model and existing code, with minimal changes to adopt our inference API (available in Python and C). Over time, you can incrementally add more MAX features for more performance, programmability, and portability.

MAX provides the following benefits for PyTorch deployments:

- Optimized performance: MAX provides advanced compiler and runtime optimizations that improve your resource efficiency and resource management. With only a few lines of code, MAX accelerates your models, reducing latency, improving user experience, and saving you valuable compute costs and resources. Relative to stock PyTorch, MAX runs PyTorch models up to 5x faster on CPU, depending on the specific workload and hardware. And GPUs are coming soon!

- Full compatibility: MAX reduces fragmentation in your workflow, by meeting you where you are now, with your existing PyTorch models, tools, and libraries. Weâve taken away the complexity in converting your Python models to high-performance languages. Instead of using brittle model translators, MAX is compatible with the PyTorch and ONNX ecosystems, which means your models work out of the box.

- Simple extensibility: MAX allows you to progressively upgrade your AI infrastructure over time, as you want to optimize the performance of your models. For even the most sophisticated AI engineers, performance-tuning AI pipelines is complicated because it involves advanced knowledge of system programming languages, AI hardware, and PyTorch itself. With MAX, youâre able to extend your models with custom operations (ops) written in Mojoâa new programming language that looks like Python and provides the performance of C. Importantly, custom ops written in Mojo are natively portable to any hardware that MAX supports and automatically fuse into the graph, ensuring peak performance.

MAX brings your PyTorch models to their full potential and scales much further to meet the demands of GenAI as it continues to rapidly evolve. MAX seamlessly integrates with your existing PyTorch models, provides an unparalleled boost in performance and efficiency, and is easily customizable, so you can focus on what you do best â innovating and creating.

### How to use MAX with PyTorch

As we mentioned earlier, there is no universal way to deploy PyTorch. As such, MAX provides a few different integration points with the PyTorch ecosystem. We detail each below.

#### MAX Engine for TorchScript Models

If you are using TorchScript, MAX Engine simplifies the process into three easy steps: load, compile, and execute.

#### MAX Engine for ONNX Models

Similarly, for ONNX models, the process is streamlined into load, compile, and execute steps.

#### Torch.compile (coming soon!)

And coming soon, we will also provide a MAX backend for PyTorch 2.xâs torch.compile API. Below is how we would optimize a Stable Diffusion model with MAX in this scenario.

Whether you're using cutting-edge PyTorch features such as torch.compile, or staying with more traditional ways of serving your models, such as TorchScript and ONNX, MAX is the best way to deploy your PyTorch workloads.

### MAX is free! Download now

By adopting MAX in your enterprise, you can take control over your PyTorch models. Join the growing community of developers who trust MAX for their model optimization needs. Don't let performance limitations hold you back. With MAX, you can elevate your PyTorch and ONNX models, delivering faster and more efficient results.

Get started with MAX today and experience the difference!

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Product

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Modular Team

Company

Our mission is to have real, positive impact in the world by reinventing the way AI technology is developed and deployed into production with a next-generation developer platform.



================================================================================

URL: https://www.modular.com/ai-resources/llm-context-evaluations

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

LLM Context Evaluations

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# LLM Context Evaluations

### Introduction

Large Language Models (LLMs) are trained on vast amounts of text data to generate language understanding and generation capabilities. One critical aspect of LLMs is their ability to handle long-rangedependencies, which are crucial for tasks like question answering, sentiment analysis, and machine translation. To evaluate an LLM's performance in handling these long-range dependencies, we need to focus onincreasing the context length.

### What is context length?

The context length refers to the maximum number of tokens (words or characters) that a model can consider when making predictions. In other words, it represents the maximum distance between two relevanttokens that the model should be able to capture.

### Why increase context length?

Increasing the context length allows LLMs to:

- Capture long-range dependencies: By considering more context, models can better understand relationships between tokens that are far apart.

- Improve accuracy: Longer contexts enable models to capture more nuanced information, leading to improved performance on tasks like question answering and sentiment analysis.

- Enhance interpretability: With longer contexts, models can provide more meaningful and interpretable results.

- Support for more modalities that require longer context length such as videos, robotics etc…

### Evaluation metrics for increasing context length

To evaluate an LLM's performance in handling longer contexts, we use various metrics that focus on the model's ability to capture long-range dependencies:

- Long-Range Dependency (LRD) scores: Measure the percentage of tokens that are correctly predicted within a certain range (e.g., 50 tokens).

- Contextualized accuracy: Calculate the accuracy of predictions made at different context lengths.

- BLEU score with longer contexts: Evaluate the similarity between generated text and reference text, using longer contexts to simulate real-world scenarios.

- ROUGE score with longer contexts: Similar to BLEU, ROUGE measures the quality of generated text, this time considering longer contexts.

- Long-range attention-based metrics:Attention mechanisms are essential for capturing long-range dependencies in LLMs. By analyzing the attention patterns and weights across different context lengths, you can evaluate how well the model is able to focus on relevant tokens at increasing distances.

- Context-awareness metrics:Context-awareness metrics assess an LLM's ability to capture subtle contextual cues that are critical for understanding longer contexts. Examples of such metrics include:Contextualized perplexity: Measures the probability of a sequence given its context.Contextualized surprisal: Evaluates how well the model can predict the next token in a sequence, considering its context.

- Contextualized perplexity: Measures the probability of a sequence given its context.

- Contextualized surprisal: Evaluates how well the model can predict the next token in a sequence, considering its context.

Attention mechanisms are essential for capturing long-range dependencies in LLMs. By analyzing the attention patterns and weights across different context lengths, you can evaluate how well the model is able to focus on relevant tokens at increasing distances.

Context-awareness metrics assess an LLM's ability to capture subtle contextual cues that are critical for understanding longer contexts. Examples of such metrics include:

- Contextualized perplexity: Measures the probability of a sequence given its context.

- Contextualized surprisal: Evaluates how well the model can predict the next token in a sequence, considering its context.

7. Task-specific evaluations:

Different NLP tasks require varying levels of contextual understanding. For instance:

- Question answering (QA): Evaluate an LLM's ability to answer questions correctly by considering longer contexts.

- Sentiment analysis: Assess an LLM's performance in identifying sentiment and emotional tone across longer texts.

To evaluate the effectiveness of a Large Language Model in handling longer contexts, you can use these metrics in combination with task-specific evaluations. This will provide a comprehensive understanding of the model's strengths and weaknesses in capturing long-range dependencies.

### Challenges:

When evaluating LLMs for context length effectiveness, keep in mind that:

- Increased computational costs: Longer contexts require more computations, which may impact evaluation time.

- Data scarcity: Gathering large datasets for longer contexts can be challenging.

- Evaluation metric limitations: Different metrics might not capture the full extent of an LLM's capabilities or weaknesses.

### Challenges in increasing context length

When increasing the context length, LLMs face several challenges:

- Computational costs: Longer contexts require more computations and memory, which can be computationally expensive.

- Training data: Large datasets are needed to train models on longer contexts.

- Evaluation metrics: New evaluation metrics need to be developed or adapted to account for the increased context length.

### Best practices for increasing context length

To successfully increase the context length of LLMs:

- Use larger batch sizes: Train models with larger batch sizes to accommodate the increased computational costs.

- Monitor memory usage: Carefully manage memory consumption during training to prevent crashes or slow-downs.

- Adapt evaluation metrics: Modify or create new evaluation metrics that account for the longer contexts.

- Experiment with different architectures: Investigate various architectural designs (e.g., attention mechanisms) that can handle longer contexts more effectively.

## Benchmark Examples

The Needle in the Haystack (NITH) benchmark is a popular tool for evaluating the ability of language models to capture long-range dependencies and contextual information. It's a challenging task that requires models to identify specific patterns or relationships within large contexts.

How NITH works:

- Seed sentence generation: A random seed sentence is generated, which serves as the starting point for the evaluation.

- Context extension: The seed sentence is extended with a varying number of tokens (e.g., 50-500) to create a long context window.

- Target token selection: A target token is randomly selected from the extended context, which represents the "needle" in the haystack.

- Model prediction: The language model is asked to predict the probability distribution over the entire vocabulary given the seed sentence and the extended context.

- Evaluation metric: The performance of the model is evaluated using a specific metric, such as perplexity or log-likelihood, which measures how well the model can predict the target token's likelihood.

Key aspects:

- Long context windows: NITH evaluates models' ability to capture contextual information within extended contexts (e.g., 50-500 tokens).

- Random seed sentences: The use of random seed sentences ensures that the evaluation is not biased towards specific topics or themes.

- Target token selection: The random selection of target tokens ensures that the model must generalize across different parts of the context, rather than relying on local patterns.

Why NITH is useful for evaluating long context windows:

- Captures contextual dependencies: NITH evaluates a language model's ability to capture complex contextual relationships and dependencies within extended contexts.

- Real-world scenario simulation: The benchmark simulates real-world scenarios where models are required to understand the context and relationships between distant tokens.



Studies have shown that state-of-the-art language models, such as BERT and RoBERTa, achieve high performance on NITH, indicating their ability to capture long-range dependencies. However, as context lengthsincrease, model performance tends to degrade, highlighting the challenges of capturing contextual information in extended contexts.

The Needle in the Haystack benchmark provides a valuable tool for evaluating language models' ability to handle long context windows and capture complex contextual relationships. By understanding how NITHworks and its key aspects, you can better appreciate the importance of this benchmark in the development of advanced language understanding capabilities.



Conclusion

Increasing the context length of Large Language Models is a crucial step in evaluating their performance on long-range dependencies. By understanding the challenges and best practices involved, researchers and developers can better equip LLMs to tackle complex language tasks.

## Next



Ring Attention with Blockwise Transformers for Near-Infinite Context

ML Systems

Rotary Position Embedding (RoPE)

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/high-performance-computing-hpc-technical-primer

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

High Performance Computing (HPC) Technical Primer

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# High Performance Computing (HPC) Technical Primer



Introduction

High-performance computing (HPC) refers to the use of powerful computer systems to solve complex problems that require significant computational resources and processing power. HPC is acritical component in various fields, including scientific research, engineering, finance, and healthcare.

### What is High-Performance Computing?

HPC is an umbrella term that encompasses several related concepts:

- Parallel Programming: The process of writing code that can run simultaneously on multiple processor cores or nodes.

- Distributed Computing: The use of a network of computers to perform computations.

- Heterogeneous Computing: The combination of different types of processing units (CPUs, GPUs, FPGAs, etc.) to achieve better performance and scalability.

### Key Characteristics

- Scalability: HPC systems are designed to scale up or down depending on the computational requirements of a problem.

- Parallelism: HPC applications often involve thousands or even millions of processing units working together in parallel.

- High-Speed Interconnects: HPC systems rely on high-speed interconnects (e.g., InfiniBand, Ethernet) to enable fast data transfer between nodes.

- Large-Scale Storage: HPC systems require large-scale storage solutions to handle massive datasets.

### Applications of High-Performance Computing

- Scientific Research: Simulations, modeling, and analysis of complex phenomena in fields like climate science, materials science, and biomedicine.

- Engineering: Design, simulation, and optimization of complex systems, such as aircraft, automobiles, and buildings.

- Finance: High-frequency trading, risk analysis, and portfolio optimization require massive computational resources.

- Healthcare: Medical imaging, genomic analysis, and personalized medicine rely on HPC to analyze large datasets.

### Challenges in High-Performance Computing

- Programming Complexity: Writing efficient parallel code is a significant challenge.

- Data Management: Handling large datasets and ensuring data integrity is crucial.

- Scalability Limitations: As systems grow larger, scalability issues can arise.

- Energy Consumption: HPC systems require significant power consumption.

### Future Directions

- Exascale Computing: The development of exascale (10^18) computing capabilities to tackle even more complex problems.

- Quantum Computing: The integration of quantum processing units into HPC systems for even greater performance gains.

- Artificial Intelligence: The application of AI and machine learning techniques to improve HPC system performance and decision-making.



### HPC Frameworks:

### CUDA

Description: CUDA is a parallel computing platform developed by NVIDIA that allows developers to use GPU acceleration in their applications.

Pros:

- High-performance acceleration on NVIDIA GPUs

- Easy integration with existing C/C++ code

- Large community of developers and extensive documentation

Cons:

- Limited portability across different GPU architectures

- May require rewriting code for CPU-based systems

- Limited support for OpenCL or other open standards

### OpenCL

Description: OpenCL is an open standard for parallel programming on heterogeneous platforms, allowing developers to write code that can run on CPUs, GPUs, and FPGAs.

Pros:

- Portable across different platforms (CPUs, GPUs, FPGAs)

- Supports multiple programming models (OpenCL, CUDA, OpenGL)

- Wide range of hardware support

Cons:

- Lower performance compared to NVIDIA-specific frameworks like CUDA

- May require more coding effort for optimal performance

- Limited community and documentation

### OpenMP

Description: OpenMP is a parallel programming standard that allows developers to write code that can run on multi-core CPUs.

Pros:

- Portable across different CPU architectures (x86, ARM, etc.)

- Supports multiple programming models (OpenMP, Pthreads)

- Wide range of hardware support

Cons:

- Limited performance benefits compared to GPU acceleration

- May require rewriting code for CPU-based systems

- Limited community and documentation

### MPI

Description: MPI (Message Passing Interface) is a standard for parallel programming on distributed-memory architectures.

Pros:

- Portable across different platforms (CPUs, GPUs, etc.)

- Supports multiple programming models (MPI, OpenMP)

- Wide range of hardware support

Cons:

- Limited performance benefits compared to GPU acceleration

- May require rewriting code for CPU-based systems

- Limited community and documentation

### Charm++

Description: Charm++ is a parallel programming framework that allows developers to write code that can run on distributed-memory architectures.

Pros:

- Portable across different platforms (CPUs, GPUs, etc.)

- Supports multiple programming models (Charm++, MPI)

- Wide range of hardware support

Cons:

- Limited performance benefits compared to GPU acceleration

- May require rewriting code for CPU-based systems

- Limited community and documentation

### UPC++

Description: UPC++ is a parallel programming framework that allows developers to write code that can run on distributed-memory architectures.

Pros:

- Portable across different platforms (CPUs, GPUs, etc.)

- Supports multiple programming models (UPC++, MPI)

- Wide range of hardware support

Cons:

- Limited performance benefits compared to GPU acceleration

- May require rewriting code for CPU-based systems

- Limited community and documentation

### CUDA Fortran

Description: CUDA Fortran is a parallel programming framework that allows developers to write Fortran code that can run on NVIDIA GPUs.

Pros:

- Easy integration with existing Fortran code

- High-performance acceleration on NVIDIA GPUs

- Large community of developers and extensive documentation

Cons:

- Limited portability across different GPU architectures

- May require rewriting code for CPU-based systems

- Limited support for OpenCL or other open standards

### HIP

Description: HIP is a parallel programming framework that allows developers to write code that can run on AMD's GPUs.

Pros:

- Easy integration with existing C/C++ code

- High-performance acceleration on AMD GPUs

- Large community of developers and extensive documentation

Cons:

- Limited portability across different GPU architectures

- May require rewriting code for CPU-based systems

- Limited support for OpenCL or other open standards

## Parallel Computing programming frameworks



### CUDA

CUDA is a parallel computing platform developed by NVIDIA that allows developers to use GPU acceleration in their applications. It's a software layer that enables the execution of CPU instructions on NVIDIAGPUs, allowing for significant performance improvements in fields such as:

- Machine Learning: Training neural networks and performing deep learning tasks.

- Computer Vision: Image processing, object detection, and recognition.

- Scientific Computing: Simulations, data analysis, and numerical computations.

- Gaming: Graphics rendering, physics engines, and game development.

CUDA provides a set of tools and APIs that allow developers to:

- Write CUDA code: Using the CUDA C/C++ compiler (nvcc), you can write programs that execute on NVIDIA GPUs.

- Access GPU memory: Directly access GPU memory using CUDA's unified virtual address space.

- Use parallel processing: Leverage multiple threads and cores within an NVIDIA GPU to perform tasks concurrently.

- Interact with the host: Communicate with the CPU (host) using APIs like CUDA-MPI or OpenMP.

CUDA is designed to work seamlessly with NVIDIA GPUs, which are optimized for massively parallel computations. By using CUDA, developers can:

- Improve performance: Take advantage of the massive parallel processing capabilities of NVIDIA GPUs.

- Simplify development: Use familiar C/C++ programming languages and APIs, rather than learning new languages or frameworks.

- Port applications: Run existing CPU-based code on NVIDIA GPUs with minimal modifications.

Overall, CUDA enables developers to tap into the immense computing power of NVIDIA GPUs, unlocking new possibilities for high-performance computing, data analysis, and artificial intelligence.

### OpenCL

Description: OpenCL is an open standard for parallel programming on heterogeneous platforms that allows developers to write code that can run on CPUs, GPUs, and FPGAs.

Pros:

- Portable across different platforms (CPUs, GPUs, etc.)

- Supports multiple programming models (OpenCL, CUDA, OpenGL)

- Wide range of hardware support

- Open standard allowing for community development

Cons:

- Lower performance compared to NVIDIA-specific frameworks like CUDA

- May require more coding effort for optimal performance

- Limited community and documentation

### ROCm

Description: ROCm is an open-source software platform developed by AMD that provides a set of tools and libraries for programming heterogeneous systems.

Pros:

- Portable across different platforms (CPUs, GPUs, etc.)

- Supports multiple programming models (OpenCL, HIP, C++ AMP)

- Wide range of hardware support

- Open source allowing for community development

Cons:

- May require rewriting code for CPU-based systems

- Limited community and documentation compared to CUDA

- Performance may not be as high as NVIDIA-specific frameworks like CUDA

### Intel MKL

Description: Intel MKL (Math Kernel Library) is a software library that provides optimized implementations of mathematical functions, including linear algebra operations.

Pros:

- Portable across different platforms (CPUs, etc.)

- Supports multiple programming models (C++, Fortran, Python)

- Wide range of hardware support

- Optimized for performance

Cons:

- May require rewriting code for CPU-based systems

- Limited support for GPU acceleration

- Performance may not be as high as NVIDIA-specific frameworks like CUDA

### Arm Compute Library

Description: The Arm Compute Library is a software library that provides optimized implementations of mathematical functions, including linear algebra operations.

Pros:

- Portable across different platforms (CPUs, etc.)

- Supports multiple programming models (C++, Fortran, Python)

- Wide range of hardware support

- Optimized for performance

Cons:

- May require rewriting code for CPU-based systems

- Limited support for GPU acceleration

- Performance may not be as high as NVIDIA-specific frameworks like CUDA

### Google's TensorFlow

Description: TensorFlow is an open-source software library developed by Google that provides a platform for building and training machine learning models.

Pros:

- Portable across different platforms (CPUs, GPUs, etc.)

- Supports multiple programming models (Python, C++, Java)

- Wide range of hardware support

- Open source allowing for community development

Cons:

- May require rewriting code for CPU-based systems

- Limited performance benefits compared to NVIDIA-specific frameworks like CUDA

- Steep learning curve for new developers

### Microsoft's C++ AMP

Description: C++ AMP (Accelerated Massive Parallelism) is a parallel programming model developed by Microsoft that provides a way to write parallel code using C++.

Pros:

- Portable across different platforms (CPUs, GPUs, etc.)

- Supports multiple programming models (C++, Fortran)

- Wide range of hardware support

- Optimized for performance

Cons:

- May require rewriting code for CPU-based systems

- Limited community and documentation compared to CUDA

- Performance may not be as high as NVIDIA-specific frameworks like CUDA

### Conclusion

High-performance computing is a critical enabler of scientific breakthroughs, technological innovations, and economic growth. As the field continues to evolve, it's essential to address the challenges and opportunities presented by the increasing complexity and scale of HPC systems.

## Next

Models

Large Language Model Technical Primer

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/ring-attention

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

Ring Attention with Blockwise Transformers for Near-Infinite Context

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# Ring Attention with Blockwise Transformers for Near-Infinite Context

Title:Ring Attention with Blockwise Transformers for Near-Infinite Context

Authors:

- Hao Liu

- Matei Zaharia

- Pieter Abbeel

### Abstract Summary

This paper introduces Ring Attention with Blockwise Transformers, a novel approach that allows handling near-infinite context lengths in Transformers. This method utilizes blockwise computation of self-attention and feedforward networks, distributing long sequences across multiple devices. This innovation enables training and inference of sequences up to the device count times longer than those managed by previous memory-efficient Transformers without additional overheads. Extensive experiments demonstrate the approach's effectiveness in language modeling and reinforcement learning tasks.

### Key Concepts

- Transformers:Backbone of many state-of-the-art AI models using self-attention and position-wise feedforward mechanisms.

- Backbone of many state-of-the-art AI models using self-attention and position-wise feedforward mechanisms.

- Memory Efficiency:Reducing memory demands of self-attention through blockwise computation without materializing the full softmax matrix.

- Reducing memory demands of self-attention through blockwise computation without materializing the full softmax matrix.

- Blockwise Computation:Dividing self-attention and feedforward networks into blocks to distribute computation and memory load across multiple devices.

- Dividing self-attention and feedforward networks into blocks to distribute computation and memory load across multiple devices.

- Ring Attention:A mechanism where devices form a ring, sending and receiving key-value blocks during computation to overlap communication and computation processes.

- A mechanism where devices form a ring, sending and receiving key-value blocks during computation to overlap communication and computation processes.

- Large Context Handling:Enabling context lengths up to device count times longer than those of prior models by efficiently managing memory and computation.

- Enabling context lengths up to device count times longer than those of prior models by efficiently managing memory and computation.

- Backbone of many state-of-the-art AI models using self-attention and position-wise feedforward mechanisms.

- Reducing memory demands of self-attention through blockwise computation without materializing the full softmax matrix.

- Dividing self-attention and feedforward networks into blocks to distribute computation and memory load across multiple devices.

- A mechanism where devices form a ring, sending and receiving key-value blocks during computation to overlap communication and computation processes.

- Enabling context lengths up to device count times longer than those of prior models by efficiently managing memory and computation.

### Problem Statement

The main problem addressed is the memory constraint in Transformers that limits their ability to handle long sequences, which is crucial for applications like video processing, long-form text analysis, and scientific data interpretation.

### Methods and Techniques

- Blockwise Parallel Transformers:Implement blockwise computation of self-attention and feedforward layers to reduce memory usage without approximations.

- Implement blockwise computation of self-attention and feedforward layers to reduce memory usage without approximations.

- Ring Attention:Devices are organized in a ring topology to overlap the communication of key-value blocks with blockwise computation, reducing memory costs and enabling large context sizes.

- Devices are organized in a ring topology to overlap the communication of key-value blocks with blockwise computation, reducing memory costs and enabling large context sizes.

- Fully Sharded Data Parallelism (FSDP):Shards the model across multiple devices to manage larger context lengths effectively.

- Shards the model across multiple devices to manage larger context lengths effectively.

- Implement blockwise computation of self-attention and feedforward layers to reduce memory usage without approximations.

- Devices are organized in a ring topology to overlap the communication of key-value blocks with blockwise computation, reducing memory costs and enabling large context sizes.

- Shards the model across multiple devices to manage larger context lengths effectively.

### Key Results

- Memory Reduction:Achieves significant memory reduction, enabling training and inference with sequence lengths up to 100 million tokens.

- Achieves significant memory reduction, enabling training and inference with sequence lengths up to 100 million tokens.

- Performance Improvement:Outperforms prior memory-efficient models by up to 512 times in context size scaling on TPUv4-1024.

- Outperforms prior memory-efficient models by up to 512 times in context size scaling on TPUv4-1024.

- Scalability:Demonstrates linear scalability of context length with the number of devices, allowing near-infinite context sizes.

- Demonstrates linear scalability of context length with the number of devices, allowing near-infinite context sizes.

- Achieves significant memory reduction, enabling training and inference with sequence lengths up to 100 million tokens.

- Outperforms prior memory-efficient models by up to 512 times in context size scaling on TPUv4-1024.

- Demonstrates linear scalability of context length with the number of devices, allowing near-infinite context sizes.

### Contributions and Innovations

- Memory Efficient Architecture:Proposes an architecture that scales context length linearly with device count, eliminating individual device memory bottlenecks.

- Proposes an architecture that scales context length linearly with device count, eliminating individual device memory bottlenecks.

- Efficient Blockwise Attention:Overlaps blockwise computation and communication in a ring topology, providing zero-overhead scaling of context size.

- Overlaps blockwise computation and communication in a ring topology, providing zero-overhead scaling of context size.

- Experimental Validation:Extensive experiments show effectiveness in language modeling and reinforcement learning, enabling handling of much longer sequences than previous models.

- Extensive experiments show effectiveness in language modeling and reinforcement learning, enabling handling of much longer sequences than previous models.

- Proposes an architecture that scales context length linearly with device count, eliminating individual device memory bottlenecks.

- Overlaps blockwise computation and communication in a ring topology, providing zero-overhead scaling of context size.

- Extensive experiments show effectiveness in language modeling and reinforcement learning, enabling handling of much longer sequences than previous models.

### Future Work

The authors suggest exploring the application of their method to video-audio-language models, extended feedback learning in reinforcement learning, scientific data analysis such as gene sequences, and complex reasoning tasks from linked data.

### Applications

- Video Processing:Analyzing long videos with high-resolution sequences.

- Analyzing long videos with high-resolution sequences.

- Text Analysis:Handling entire books or large documents for comprehensive text analysis.

- Handling entire books or large documents for comprehensive text analysis.

- Scientific Research:Processing complex datasets in scientific experiments, such as gene sequences or high-dimensional data.

- Processing complex datasets in scientific experiments, such as gene sequences or high-dimensional data.

- Code Analysis:Understanding and generating codebases by analyzing extensive code sequences.

- Understanding and generating codebases by analyzing extensive code sequences.

- Analyzing long videos with high-resolution sequences.

- Handling entire books or large documents for comprehensive text analysis.

- Processing complex datasets in scientific experiments, such as gene sequences or high-dimensional data.

- Understanding and generating codebases by analyzing extensive code sequences.

### Relevant Links

- Code Repository: https://github.com/lhao499/llm_large_context

## Next



Attention with Linear Biases Enables Input Length Extrapolation (ALiBi)



YaRN: Efficient Context Window Extension of Large Language Models



LLM Context Evaluations

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/alibi

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

Attention with Linear Biases Enables Input Length Extrapolation (ALiBi)

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# Attention with Linear Biases Enables Input Length Extrapolation (ALiBi)

Title: Train Short, Test Long: Attention with Linear Biases Enables Input Length Extrapolation

Authors: Ofir Press, Noah A. Smith, Mike Lewis

### Abstract Summary:

The paper addresses the question of how transformer models can extrapolate to longer sequences during inference than those seen during training. The authors propose a new method called Attention with Linear Biases (ALiBi), which biases the attention scores based on the distance between query and key tokens. This method enables efficient extrapolation, achieving similar perplexity to sinusoidal position embeddings but with faster training and less memory usage. ALiBi's inductive bias towards recency allows it to outperform other position methods on the WikiText-103 benchmark.

### Key Concepts:

- Extrapolation in Transformers: The ability of a model to perform well on input sequences longer than those seen during training.

- Position Embeddings: Methods to encode positional information into transformer models, traditionally using sinusoidal or learned embeddings.

- Attention with Linear Biases (ALiBi): A new method that biases attention scores with a penalty proportional to the distance between tokens, eliminating the need for positional embeddings and enabling efficient extrapolation.

- Perplexity: A measurement of how well a probabilistic model predicts a sample, with lower perplexity indicating better performance.

- WikiText-103 Benchmark: A dataset used for evaluating language models.

### Problem Statement:

The main problem addressed is how to enable transformer models to extrapolate efficiently to longer sequences during inference than those encountered during training, without incurring significant computational overhead or memory usage.

### Methods and Techniques:

- ALiBi Method: Introduces a linear bias to attention scores based on token distance. This bias is a fixed penalty that increases linearly with distance, allowing models to handle longer sequences efficiently.

- Sinusoidal Position Embeddings: Traditional method where positional information is added to word embeddings using sinusoidal functions.

- Rotary Position Embeddings: Another method that multiplies keys and queries by sinusoidal embeddings at each layer.

- T5 Bias: Modifies attention values by adding a learned, shared bias dependent on the distance between tokens.

### Key Results:

- ALiBi models trained on shorter sequences can extrapolate to much longer sequences without a significant drop in performance.

- A 1.3 billion parameter model trained on 1024 tokens with ALiBi achieved the same perplexity on 2048-token sequences as a sinusoidal model trained on 2048 tokens, but trained 11% faster and used 11% less memory.

- ALiBi outperforms sinusoidal, rotary, and T5 position methods on the WikiText-103 benchmark, maintaining strong performance even on very long sequences (up to 10,000 tokens).

### Contributions and Innovations:

- Efficient Extrapolation: ALiBi allows transformer models to extrapolate efficiently, reducing the need for longer training sequences and saving computational resources.

- Inductive Bias Towards Recency: ALiBi’s bias towards more recent tokens improves performance on tasks where recent context is more relevant.

- Implementation Simplicity: ALiBi can be implemented with minimal changes to existing transformer code, making it easy to adopt.

### Future Work:

The authors suggest exploring further improvements in extrapolation efficiency and applying ALiBi to other tasks and models. They also propose combining ALiBi with other recent innovations in transformer models to achieve even better performance.

### Applications:

- Language Modeling: Improving the performance and efficiency of large-scale language models.

- Text Generation: Enabling models to generate longer and more coherent text.

- Machine Translation: Applying ALiBi to translation models to handle longer input and output sequences effectively.

### Relevant Links:

- Code & models

## Next

ML Systems

Rotary Position Embedding (RoPE)



Ring Attention with Blockwise Transformers for Near-Infinite Context

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/yarn

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

YaRN: Efficient Context Window Extension of Large Language Models

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# YaRN: Efficient Context Window Extension of Large Language Models

Title: YaRN: Efficient Context Window Extension of Large Language ModelsAuthors: Bowen Peng, Jeffrey Quesnelle, Honglu Fan, Enrico Shippole

### Abstract Summary:

YaRN (Yet another RoPE extensioN method) is a compute-efficient method for extending the context window of large language models using Rotary Position Embeddings (RoPE). It achieves this with significantly fewer tokens and training steps than previous methods. The method allows models like LLaMA to extrapolate to much longer context lengths while surpassing previous state-of-the-art techniques. The fine-tuned models have been tested up to a 128k context length and are available online.

### Key Concepts:

- Rotary Position Embeddings (RoPE): A method to encode positional information in transformer-based models effectively.

- Context Window Extension: Techniques to enable language models to handle sequences longer than those seen during pre-training.

- Position Interpolation (PI): A method to stretch positional encodings to extend the context window.

- NTK-aware Interpolation: Adjusts scaling to preserve high-frequency information in RoPE, avoiding the loss of detail in extended contexts.

- Dynamic NTK Interpolation: Dynamically adjusts scaling during inference to extend the context window without fine-tuning.

- YaRN Method: Combines NTK-by-parts interpolation and attention scaling to achieve state-of-the-art performance in context window extension.

### Problem Statement:

The main problem addressed by this paper is the limitation of transformer-based language models to generalize beyond the context window length they were trained on. Existing models struggle to handle sequences longer than their pre-training lengths, limiting their applicability to tasks requiring long-range context understanding.

### Methods and Techniques:

- Position Interpolation (PI): Scales positional indices to stretch the context window, requiring fine-tuning on fewer tokens.

- NTK-aware Interpolation: Uses a base change in RoPE to avoid losing high-frequency information, improving performance on non-fine-tuned models.

- Dynamic Scaling: Dynamically updates the scale factor during inference to prevent performance degradation at longer context lengths.

- NTK-by-parts Interpolation: Selectively interpolates RoPE dimensions based on their relative frequencies, preserving local relationships in embeddings.

- Attention Scaling: Adjusts the attention mechanism with a temperature parameter to maintain low perplexity across extended contexts.

### Key Results:

- Perplexity Performance: YaRN achieves lower perplexity scores compared to other methods, maintaining strong performance up to 128k context lengths.

- Passkey Retrieval Task: YaRN models show high accuracy (>99%) in retrieving passkeys across extended context lengths.

- Benchmark Performance: Minimal performance degradation in standardized benchmarks, maintaining near-baseline scores even at extended contexts.

### Contributions and Innovations:

- Efficient Training: YaRN achieves context extension with 10x fewer tokens and 2.5x fewer training steps.

- State-of-the-Art Performance: Outperforms previous methods in both fine-tuned and non-fine-tuned scenarios.

- Practical Implementation: Compatible with libraries like Flash Attention 2, making it easy to integrate into existing systems.

### Future Work:

The authors suggest exploring further optimizations for the YaRN method and extending its applicability to other models and tasks. They also propose investigating the theoretical underpinnings of attention scaling and its impact on model performance across different architectures.

### Applications:

- Long Document Summarization: Efficiently handle and summarize documents that exceed typical context lengths.

- Autoregressive Text Generation: Generate coherent text over extended sequences without degradation in quality.

- Legal and Medical Text Analysis: Process and analyze lengthy legal documents or medical records requiring long-range context understanding.

### Relevant Links:

- GitHub Repository: YaRN Models

## Next

ML Systems

Rotary Position Embedding (RoPE)



Ring Attention with Blockwise Transformers for Near-Infinite Context

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/g

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

Gemini: A Family of Highly Capable Multimodal Models

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# Gemini: A Family of Highly Capable Multimodal Models

Title: Gemini: A Family of Highly Capable Multimodal Models

Authors: Gemini Team, Google

### Abstract Summary:

The paper introduces the Gemini family of multimodal models that demonstrate advanced capabilities across image, audio, video, and text understanding. The models come in various sizes: Ultra, Pro, and Nano, each tailored for different applications. The most advanced model, Gemini Ultra, achieves state-of-the-art performance in 30 out of 32 benchmarks, including human-expert performance on the MMLU exam benchmark. The paper discusses the model architecture, training methods, evaluation results, and potential applications of these models in various fields.

### Key Concepts:

- Multimodal Models: Gemini models can process and understand multiple types of data, including images, audio, video, and text.

- Model Sizes: The Gemini family includes Ultra, Pro, and Nano models, optimized for different tasks and computational constraints.

- State-of-the-Art Performance: Gemini Ultra advances the state of the art in 30 out of 32 benchmarks, including achieving human-expert performance on MMLU.

- Training and Post-Training: The models undergo large-scale pre-training followed by targeted post-training to enhance specific capabilities.

- Applications: Gemini models are designed for a wide range of applications, from complex reasoning tasks to on-device use cases.

### Problem Statement:

The main problem addressed by this paper is developing a family of multimodal models that can exhibit strong generalist capabilities across different data modalities (image, audio, video, and text) while achieving state-of-the-art performance in specific tasks within each modality.

### Methods and Techniques:

- Model Architecture: Gemini models use Transformer decoders with enhancements for stable training at scale and optimized inference.

- Pre-Training and Post-Training: Initial large-scale pre-training is followed by post-training to enhance quality and ensure alignment with safety criteria.

- Multimodal Input Handling: The models are trained to handle interleaved textual, audio, and visual inputs and produce multimodal outputs.

- Efficiency Improvements: Innovations in distillation and quantization enable efficient deployment of smaller models like Gemini Nano.

### Key Results:

- Benchmark Performance: Gemini Ultra achieves state-of-the-art results in 30 out of 32 benchmarks, including notable advances in text, image, video, and audio tasks.

- MMLU Benchmark: First model to achieve human-expert performance on the MMLU exam benchmark with a score above 90%.

- Multimodal Reasoning: Strong performance in tasks requiring cross-modal understanding and reasoning.

- Comparative Benchmarks: Outperforms existing models such as GPT-4 and PaLM 2 in various benchmarks including MMLU, GSM8K, and HumanEval.

### Contributions and Innovations:

- Multimodal Capabilities: Demonstrates the ability to understand and generate responses across different data types.

- Efficiency in Training: Uses scalable training infrastructure and algorithms to handle large-scale model training efficiently.

- Model Variants: Offers multiple model sizes to cater to different computational and application needs.

- Post-Training Enhancements: Improves model performance and safety through targeted post-training methods.

### Future Work:

The authors suggest further exploration of:

- Enhancing model capabilities in underrepresented languages and low-resource tasks.

- Developing more robust and nuanced evaluation benchmarks.

- Expanding the use of Gemini models in practical applications and integrating them with external tools and services.

### Applications:

- Education: Using multimodal reasoning capabilities to assist in educational settings, such as verifying solutions to complex problems.

- Coding: Advanced code generation and competitive programming assistance.

- On-Device Applications: Deploying efficient models for tasks like summarization and reading comprehension on mobile devices.

- Conversational AI: Enhancing user interactions through improved conversational capabilities in services like Google AI Studio and Cloud Vertex AI.

### Relevant Links:

- https://gemini.google.com

## Next



Gemini 1.5: Unlocking multimodal understanding across millions of tokens of context

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/gemini-pro-1-5

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

Gemini 1.5: Unlocking multimodal understanding across millions of tokens of context

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# Gemini 1.5: Unlocking multimodal understanding across millions of tokens of context

### Title and Authors:

Gemini 1.5: Unlocking multimodal understanding across millions of tokens of context" by the Gemini Team from Google.

### Abstract Summary:

The paper presents Gemini 1.5 Pro, a highly compute-efficient multimodal mixture-of-experts model. It excels in recalling and reasoning over extensive contexts, handling millions of tokens, including long documents, videos, and audio. The model achieves near-perfect recall on long-context tasks and surpasses previous state-of-the-art models. It demonstrates significant improvements in long-context capabilities, achieving next-token prediction and retrieval performance up to 10 million tokens.

### Key Concepts:

- Multimodal Mixture-of-Experts (MoE) Model: Combines multiple expert models to handle different parts of input data, improving efficiency and performance.

- Long-Context Retrieval: Capable of recalling and reasoning over contexts up to 10 million tokens.

- Multimodal Capabilities: Processes text, video, and audio inputs simultaneously.

- Next-Token Prediction: Enhanced performance in predicting subsequent tokens in long contexts.

- In-Context Learning: Learns new tasks and languages from extensive contextual information provided during inference.

### Problem Statement:

The main problem addressed by the paper is the challenge of efficiently processing and reasoning over extremely long contexts across multiple modalities (text, video, audio) in large language models.

### Methods and Techniques:

- Sparse Mixture-of-Experts (MoE) Architecture: Utilizes a routing function to activate only a subset of model parameters for each input, enhancing efficiency and scalability.

- Training Infrastructure: Trained on Google’s TPUv4 accelerators with a diverse dataset including text, code, image, audio, and video content.

- Instruction Tuning: Finetuning the model on multimodal data paired with instructions and human preference data to improve performance.

### Key Results:

- Near-Perfect Recall: Achieves over 99% recall up to 10 million tokens in all modalities.

- Performance Benchmarks: Outperforms previous models like Gemini 1.0 Ultra on various benchmarks, including long-document QA, video QA, and automatic speech recognition (ASR).

- In-Context Learning: Successfully learns to translate from English to Kalamang, a low-resource language, using only reference materials provided in context.

### Contributions and Innovations:

- Long-Context Processing: Demonstrates significant improvements in handling and retrieving information from extremely long contexts.

- Multimodal Integration: Efficiently integrates and processes text, video, and audio inputs in a single model.

- Efficiency and Scalability: Achieves high performance with significantly less training compute compared to previous models, making it more efficient for practical deployment.

### Future Work:

The authors suggest further exploring the limits of long-context understanding, improving the model's efficiency, and extending its capabilities to support more complex and diverse real-world applications.

### Applications:

- Long-Document Question Answering: Enhances the ability to answer questions from extensive documents.

- Video and Audio Analysis: Improves performance in tasks requiring comprehension and reasoning over long video and audio recordings.

- Language Translation: Facilitates translation and learning of low-resource languages using in-context learning.

### Relevant Links:

- Kalamang language

- Paul Graham articles

- DeepMind Gemini

## Next

Models

Gemma: Open Models Based on Gemini Research and Technology



Gemini: A Family of Highly Capable Multimodal Models

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/efficient-memory-management-for-large-language-model-serving-with-pagedattention

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

Efficient Memory Management for LLM Serving with PagedAttention

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# Efficient Memory Management for LLM Serving with PagedAttention

Title: Efficient Memory Management for Large Language Model Serving with PagedAttention

Authors: Woosuk Kwon, Zhuohan Li, Siyuan Zhuang, Ying Sheng, Lianmin Zheng, Cody Hao Yu, Joseph E. Gonzalez, Hao Zhang, Ion Stoica.

### Abstract Summary:

The paper proposes PagedAttention, an attention algorithm inspired by virtual memory and paging techniques in operating systems, to address inefficiencies in managing the key-value cache (KV cache) memory for large language models (LLMs). The resulting system, vLLM, achieves near-zero waste in KV cache memory and flexible sharing within and across requests, improving throughput by 2-4x compared to existing systems like FasterTransformer and Orca.

### Key Concepts:

- PagedAttention algorithm

- Virtual memory and paging techniques

- Key-value cache (KV cache) memory

- vLLM serving system

- High throughput and low latency

- Memory fragmentation

- Dynamic memory allocation

- Distributed execution

- Parallel sampling and beam search

- Memory sharing

### Problem Statement:

The main problem addressed by this paper is the inefficient management of KV cache memory in large language model serving systems, which leads to significant memory waste, limits batch size, and reduces throughput.

Methods and Techniques:

- PagedAttention Algorithm: Inspired by virtual memory and paging in operating systems, it partitions the KV cache into blocks that are not stored in contiguous memory, allowing more flexible and efficient memory management.

- vLLM System: A serving system built on top of PagedAttention, featuring block-level memory management and preemptive request scheduling, supporting popular LLMs and distributed execution.

- Centralized Scheduler: Coordinates the execution of distributed GPU workers, ensuring efficient memory usage and high throughput.

- KV Cache Manager: Manages the physical KV cache memory through instructions from the centralized scheduler, allowing dynamic memory allocation and efficient memory sharing.

- Copy-on-Write Mechanism: For handling parallel sampling and beam search, allowing shared memory to be copied only when modified.

- Fine-Grained Batching and Scheduling: Enables efficient processing of multiple requests with varying input and output lengths without significant queuing delays or memory wastage.

### Key Results:

- vLLM improves LLM serving throughput by 2-4x compared to FasterTransformer and Orca, without affecting model accuracy.

- The improvements are more pronounced with longer sequences, larger models, and more complex decoding algorithms.

- vLLM achieves 1.67x to 3.58x higher throughput in translation tasks with shared prefixes compared to Orca.

- vLLM demonstrates significant memory savings (up to 55.2%) in beam search scenarios and higher throughput in chatbot applications.

### Contributions and Innovations:

- Introduction of PagedAttention, enabling non-contiguous storage of KV cache and reducing memory fragmentation.

- Development of vLLM, a high-throughput distributed LLM serving system with near-zero memory waste.

- Implementation of effective memory sharing techniques for parallel sampling and beam search, significantly reducing memory usage.

- Enhanced scheduling and preemption strategies to handle variable input and output lengths efficiently.

- Support for various decoding algorithms and mixed decoding methods within the same batch, increasing overall throughput.

### Future Work:

The authors suggest exploring further optimizations in kernel-level operations, expanding support for more complex LLMs and additional decoding algorithms, and improving the system’s adaptability to different hardware configurations.

Applications:

- Programming Assistants: Enhancing coding efficiency by generating multiple code suggestions in parallel.

- Chatbots: Providing more responsive and cost-effective conversational agents by improving memory management and throughput.

- Machine Translation: Offering high-quality translations with complex decoding methods like beam search, benefiting from shared prefix techniques.

- Content Generation: Enabling efficient generation of long and complex text outputs for applications in marketing, entertainment, and education.

- Cloud Services: Reducing operational costs and improving performance for cloud-based LLM services, facilitating wider adoption and scalability.

### Relevant Links:

- vLLM source code

- Paper link

- Modular intro to Serving

## Next

ML Systems

AI & Memory Wall

ML Systems

FlashAttention

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/mlir-a-compiler-infrastructure-for-the-end-of-moore-s-law

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

MLIR: A Compiler Infrastructure for the End of Mooreâs Law

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# MLIR: A Compiler Infrastructure for the End of Mooreâs Law

Title: MLIR: A Compiler Infrastructure for the End of Moore’s Law

Authors: Chris Lattner (Google), Mehdi Amini (Google), Uday Bondhugula (IISc), Albert Cohen (Google), Andy Davis (Google), Jacques Pienaar (Google), River Riddle (Google), Tatiana Shpeisman (Google), Nicolas Vasilache (Google), Oleksandr Zinenko (Google)

### Abstract Summary:

MLIR presents a novel compiler infrastructure designed to address software fragmentation, improve compilation for heterogeneous hardware, and reduce the cost of building domain-specific compilers. It aims to facilitate the design and implementation of code generators, translators, and optimizers across various levels of abstraction and application domains.

### Key Concepts:

- Reusable and extensible compiler infrastructure

- Heterogeneous hardware support

- Domain-specific compilers

- Intermediate Representation (IR)

- Static Single Assignment (SSA)

- Declarative system for IR dialects

- Multithreaded compilation support

- Progressive lowering

### Problem Statement:

The main problem addressed by the paper is the high cost and complexity of building and maintaining domain-specific compilers and the lack of a unified infrastructure that can efficiently handle heterogeneous hardware and various levels of abstraction in compiler design.

### Methods and Techniques:

- Static Single Assignment (SSA): Standardizing SSA-based IR data structures to simplify dataflow analysis and transformations.

- IR Dialects: Providing a declarative system for defining various IR dialects to support different abstraction levels and domains.

- Common Infrastructure: Offering a wide range of common compiler infrastructure components, such as parsing and printing logic, location tracking, and pass management, to reduce the engineering effort.

- Progressive Lowering: Allowing for gradual lowering from high-level representations to low-level code, facilitating better optimization and support for heterogeneous targets.

### Key Results:

- Reduction in Cost: MLIR significantly reduces the cost of developing compilers by providing reusable components and infrastructure.

- Support for Heterogeneity: It efficiently supports heterogeneous compilation by enabling the creation of IRs that span different abstraction levels and hardware targets.

- Improved Compilation: The framework shows improved compilation performance and capability through diverse use cases, such as TensorFlow graph optimizations and polyhedral code generation.

### Contributions and Innovations:

- Extensible Compiler Infrastructure: MLIR introduces a novel, extensible framework that supports the creation and evolution of domain-specific compilers.

- Dialect Support: It allows for the easy definition of new IR dialects, which can be mixed and matched within the same compilation pipeline.

- Reusability: The infrastructure supports reusable passes and transformations, enhancing efficiency and consistency across different compiler projects.

### Future Work:

The authors suggest further research into areas such as:

- Enhanced support for more complex data structures and programming models.

- Integration with higher-level languages and more advanced IR designs.

- Further improvements in parallel and heterogeneous compilation.

### Applications:

- Machine Learning Frameworks: Optimizing TensorFlow graphs for various hardware targets.

- High-Performance Computing: Applying polyhedral code generation techniques.

- Domain-Specific Compilers: Facilitating the creation of compilers for specific tasks, such as lattice regression.

### Relevant Links:

- MLIR website

- Mojo API for MLIR



## Next

ML Systems

ML Compiler Technical Primer

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/roberta-a-robustly-optimized-bert-pretraining-approach

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

RoBERTa: A Robustly Optimized BERT Pretraining Approach

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# RoBERTa: A Robustly Optimized BERT Pretraining Approach

Title: RoBERTa: A Robustly Optimized BERT Pretraining Approach

Authors: Yinhan Liu, Myle Ott, Naman Goyal, Jingfei Du, Mandar Joshi, Danqi Chen, Omer Levy, Mike Lewis, Luke Zettlemoyer, Veselin Stoyanov

### Abstract Summary:

RoBERTa is a replication study of BERT pretraining that focuses on the impact of various hyperparameters and training data sizes. It demonstrates that BERT was undertrained and proposes an improved training method that achieves state-of-the-art results on GLUE, RACE, and SQuAD benchmarks.

### Key Concepts:

- BERT pretraining

- Hyperparameter tuning

- Training data size

- Next sentence prediction (NSP)

- Masked language modeling (MLM)

- GLUE, RACE, and SQuAD benchmarks

- Dynamic masking

- Transformer architecture

### Problem Statement:

The paper addresses the challenge of optimizing BERT pretraining by carefully evaluating the impact of hyperparameter choices and training data size, aiming to understand which aspects contribute most to performance improvements.

### Methods and Techniques:

- Extended Training Duration: Training the model for a longer time with bigger batches and more data.

- Removing Next Sentence Prediction (NSP): The NSP objective was found unnecessary and was removed to improve performance.

- Training on Longer Sequences: Increasing the maximum sequence length for training.

- Dynamic Masking: Changing the masking pattern applied to the training data dynamically rather than using a fixed mask throughout the training.

- Large Dataset Collection: Collecting a large new dataset (CC-NEWS) comparable to other privately used datasets to control for training set size effects.

### Key Results:

- RoBERTa surpasses the original BERT model's performance on various benchmarks.

- Achieves state-of-the-art results on GLUE, RACE, and SQuAD benchmarks.

- Dynamic masking provides slight performance improvements over static masking.

- Removing NSP does not harm and sometimes improves downstream task performance.

- Using larger batches improves optimization speed and end-task performance.

### Contributions and Innovations:

- Pretraining Strategy Improvements: Simple yet effective changes to the BERT pretraining procedure lead to significant performance gains.

- Dataset Collection: Introduction of the CC-NEWS dataset for better control over training data size effects.

- Benchmark Results: RoBERTa achieves state-of-the-art results on multiple benchmarks without the need for multi-task finetuning or additional data augmentation.

- Code Release: The models and code for RoBERTa are made publicly available, facilitating replication and further research.

### Future Work:

The authors suggest further exploration of large batch training and architectural changes, as well as a deeper analysis of the effects of data size and diversity on pretraining.

### Applications:

- Natural Language Understanding: Improving performance on tasks like sentiment analysis, text classification, and question answering.

- Machine Translation: Enhancing translation quality by using robustly pretrained models.

- Information Retrieval: Better document and query matching in search engines.

- Conversational AI: Enhancing the capabilities of chatbots and virtual assistants.

### Relevant Links:

- RoBERTa models and code

- RoBERTa paper on arXiv

- RoBERTa inference example running on MAX



## Next

Models

Gemma: Open Models Based on Gemini Research and Technology

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/large-langu

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

Large Language Model Technical Primer

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# Large Language Model Technical Primer

## Technical Primer on Large Language Models

### Introduction

Large Language Models (LLMs) are a type of artificial intelligence model designed to understand and generate human language. These models are typically based on deep learning architectures, such as the Transformer, and are trained on vast amounts of text data. LLMs have revolutionized natural language processing (NLP) by achieving state-of-the-art performance in a variety of tasks, including text generation, translation, summarization, and question answering.

### Architecture

The architecture of most LLMs is based on the Transformer model, introduced by Vaswani et al. in their seminal 2017 paper, "Attention is All You Need." The Transformer architecture relies on a mechanism called self-attention, which allows the model to weigh the importance of different words in a sentence when making predictions. This mechanism is more efficient than previous recurrent or convolutional architectures, enabling the training of much larger models.

### Key Components:

- Embeddings: The input text is converted into continuous vector representations, known as embeddings. These embeddings capture semantic information about the words.

- Attention Mechanisms: Self-attention layers compute attention scores between all pairs of words in a sentence, allowing the model to focus on relevant context when making predictions.

- Feedforward Neural Networks: These layers process the attended information and produce the final output of each layer.

- Positional Encoding: Since the Transformer does not have a built-in notion of word order, positional encodings are added to the embeddings to provide information about the position of words in a sentence.

- Stacked Layers: Multiple layers of attention and feedforward networks are stacked to increase the model's capacity to learn complex patterns.

### Training

Training LLMs involves optimizing the model's parameters to minimize the difference between its predictions and the actual text. This process typically requires massive datasets and significant computational resources. Common training objectives include:

- Language Modeling: The model is trained to predict the next word in a sentence, given the previous words. This can be done using techniques like masked language modeling (MLM) or autoregressive language modeling (ALM).

- Sequence-to-Sequence Learning: For tasks like translation, the model is trained to map input sequences (e.g., sentences in one language) to output sequences (e.g., sentences in another language).

### Applications

LLMs have numerous applications across various domains:

- Text Generation: Generating coherent and contextually relevant text, such as stories, articles, and code.

- Machine Translation: Translating text from one language to another with high accuracy.

- Summarization: Condensing long documents into concise summaries while preserving key information.

- Question Answering: Providing accurate answers to questions based on context from provided text.

- Sentiment Analysis: Determining the sentiment expressed in a piece of text, useful for market analysis and customer feedback.

### Challenges

Despite their impressive capabilities, LLMs face several challenges:

- Resource Intensiveness: Training and deploying LLMs require substantial computational power and memory.

- Bias and Fairness: LLMs can inherit and amplify biases present in their training data, leading to biased or unfair outcomes.

- Interpretability: Understanding the decisions made by LLMs is challenging due to their complexity, making it difficult to debug and trust their outputs.

- Data Privacy: Training on large datasets may inadvertently include sensitive information, raising privacy concerns.

### Future Directions

Research in LLMs continues to evolve, with several promising directions:

- Efficient Training: Developing methods to reduce the computational requirements for training LLMs, such as distillation, pruning, and quantization.

- Bias Mitigation: Creating techniques to identify and mitigate biases in LLMs to ensure fairer and more equitable outcomes.

- Improved Interpretability: Enhancing the interpretability of LLMs to make their decisions more transparent and trustworthy.

- Multimodal Models: Extending LLMs to handle multiple types of data, such as combining text with images or audio, to create more versatile AI systems.

### Conclusion

Large Language Models represent a significant advancement in the field of natural language processing, offering powerful tools for understanding and generating human language. While they come with challenges, ongoing research and innovation are continually improving their capabilities and addressing their limitations. As LLMs continue to evolve, they hold the potential to transform a wide range of applications and industries.

## Next

Models

Llama 2



High Performance Computing (HPC) Technical Primer

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/contrastive

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

Contrastive Language-Image Pre-training (CLIP)

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# Contrastive Language-Image Pre-training (CLIP)

### Title and Authors:

Title: Learning Transferable Visual Models From Natural Language Supervision

Authors: Alec Radford, Jong Wook Kim, Chris Hallacy, Aditya Ramesh, Gabriel Goh, Sandhini Agarwal, Girish Sastry, Amanda Askell, Pamela Mishkin, Jack Clark, Gretchen Krueger, Ilya Sutskever

### Abstract Summary:

The paper presents a method for training state-of-the-art image representations by predicting which caption corresponds to which image using a dataset of 400 million image-text pairs. This approach enables zero-shot transfer of the model to various downstream tasks, achieving competitive performance with fully supervised models without needing task-specific training data.

### Key Concepts:

- Natural Language Supervision

- Contrastive Pre-training

- Zero-shot Learning

- Image-Text Pairing

- Transfer Learning

- CLIP (Contrastive Language-Image Pre-training)

### Problem Statement:

The main problem addressed in the paper is the limitation of current computer vision systems, which are trained to predict a fixed set of predetermined object categories, thus requiring additional labeled data for new visual concepts. The paper explores learning directly from raw text about images to leverage a broader source of supervision.

Methods and Techniques:

- Dataset Creation: A new dataset of 400 million image-text pairs collected from the internet was created to cover a broad set of visual concepts.

- Pre-training Task: The model is pre-trained to predict which caption corresponds to which image in a batch, optimizing a symmetric cross-entropy loss over the similarity scores between image and text embeddings.

- Model Architecture: The image encoder is a ResNet-50 or Vision Transformer (ViT), and the text encoder is a Transformer model. Both encoders are trained to project their respective inputs into a shared embedding space.

- Contrastive Objective: Instead of predicting the exact words in the text, the model predicts the correct image-text pairings from a batch of possible pairs, using a contrastive objective to maximize the cosine similarity of correct pairs and minimize that of incorrect pairs.

- Training Efficiency: The model uses techniques like gradient checkpointing, mixed-precision training, and large minibatch sizes to efficiently handle the large-scale dataset.

### Key Results:

- The model achieves competitive performance on over 30 computer vision datasets in tasks such as OCR, action recognition in videos, geo-localization, and fine-grained object classification.

- Zero-shot transfer performance matches the accuracy of a ResNet-50 on ImageNet without using any of the 1.28 million training examples.

- The model shows improved efficiency in zero-shot transfer compared to previous methods, learning 3-4 times faster.

### Contributions and Innovations:

- Demonstrated the effectiveness of learning image representations directly from natural language supervision at a large scale.

- Introduced a new large-scale dataset of image-text pairs.

- Developed an efficient pre-training method using a contrastive objective, leading to significant improvements in zero-shot transfer performance.

- Showcased the ability to perform various tasks with minimal or no task-specific training data.

### Future Work:

The authors suggest further exploration of pre-training methods and architectures to improve the efficiency and performance of models trained with natural language supervision. They also propose investigating the integration of additional types of data and tasks to enhance the generality and robustness of the models.

Applications:

- Content-Based Image Retrieval: Leveraging natural language descriptions to retrieve images based on textual queries.

- Zero-shot Image Classification: Classifying images into categories not seen during training using natural language descriptions of the categories.

- Multimodal Search Engines: Combining image and text data to improve search capabilities in online platforms.

- Enhanced Assistive Technologies: Improving the accuracy and generality of assistive technologies that rely on image recognition.

### Relevant Links:

- Code and Pre-trained Model Weights



## Next

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/mac

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

ML Compiler Technical Primer

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# ML Compiler Technical Primer

## Technical Primer on Machine Learning Compilers

### Introduction

A Machine Learning Compiler (MLC) is a specialized software tool designed to optimize and transform machine learning models into efficient executable code for deployment on various hardware platforms. The goal of an MLC is to bridge the gap between high-level machine learning frameworks and the low-level hardware-specific code, ensuring that models run efficiently on different devices, from CPUs and GPUs to specialized accelerators like TPUs (Tensor Processing Units).

### Key Components of a Machine Learning Compiler

- FrontendModel Ingestion: The frontend takes in machine learning models from high-level frameworks such as TensorFlow, PyTorch, or ONNX. It parses the model and converts it into an intermediate representation (IR) that can be further optimized and transformed.Intermediate Representation (IR): The IR is a hardware-agnostic representation of the model that serves as a common language between the frontend and the backend of the compiler. Examples include XLA (Accelerated Linear Algebra) for TensorFlow and TorchScript for PyTorch.

- Model Ingestion: The frontend takes in machine learning models from high-level frameworks such as TensorFlow, PyTorch, or ONNX. It parses the model and converts it into an intermediate representation (IR) that can be further optimized and transformed.

- Intermediate Representation (IR): The IR is a hardware-agnostic representation of the model that serves as a common language between the frontend and the backend of the compiler. Examples include XLA (Accelerated Linear Algebra) for TensorFlow and TorchScript for PyTorch.

- Optimization PassesGraph Optimization: This involves optimizing the computation graph of the model to reduce redundancy, fuse operations, and minimize memory usage. Techniques such as constant folding, operation fusion, and common subexpression elimination are applied.Quantization: Reducing the precision of model parameters and computations (e.g., from 32-bit floating point to 8-bit integers) to improve performance and reduce memory footprint while maintaining acceptable accuracy.Pruning and Sparsity: Removing less significant weights and exploiting sparsity in the model to speed up computation and reduce resource usage.

- Graph Optimization: This involves optimizing the computation graph of the model to reduce redundancy, fuse operations, and minimize memory usage. Techniques such as constant folding, operation fusion, and common subexpression elimination are applied.

- Quantization: Reducing the precision of model parameters and computations (e.g., from 32-bit floating point to 8-bit integers) to improve performance and reduce memory footprint while maintaining acceptable accuracy.

- Pruning and Sparsity: Removing less significant weights and exploiting sparsity in the model to speed up computation and reduce resource usage.

- BackendTarget-Specific Code Generation: The backend transforms the optimized IR into hardware-specific code. This involves generating low-level code for CPUs, GPUs, TPUs, or other accelerators.Kernel Fusion and Scheduling: Combining multiple small operations into a single kernel to reduce memory accesses and improve performance. Scheduling determines the order of operations to maximize hardware utilization and minimize latency.Memory Management: Efficiently allocating and managing memory resources, including handling tensor layouts and data movement between different memory hierarchies (e.g., CPU cache, GPU memory).

- Target-Specific Code Generation: The backend transforms the optimized IR into hardware-specific code. This involves generating low-level code for CPUs, GPUs, TPUs, or other accelerators.

- Kernel Fusion and Scheduling: Combining multiple small operations into a single kernel to reduce memory accesses and improve performance. Scheduling determines the order of operations to maximize hardware utilization and minimize latency.

- Memory Management: Efficiently allocating and managing memory resources, including handling tensor layouts and data movement between different memory hierarchies (e.g., CPU cache, GPU memory).

- Model Ingestion: The frontend takes in machine learning models from high-level frameworks such as TensorFlow, PyTorch, or ONNX. It parses the model and converts it into an intermediate representation (IR) that can be further optimized and transformed.

- Intermediate Representation (IR): The IR is a hardware-agnostic representation of the model that serves as a common language between the frontend and the backend of the compiler. Examples include XLA (Accelerated Linear Algebra) for TensorFlow and TorchScript for PyTorch.

- Graph Optimization: This involves optimizing the computation graph of the model to reduce redundancy, fuse operations, and minimize memory usage. Techniques such as constant folding, operation fusion, and common subexpression elimination are applied.

- Quantization: Reducing the precision of model parameters and computations (e.g., from 32-bit floating point to 8-bit integers) to improve performance and reduce memory footprint while maintaining acceptable accuracy.

- Pruning and Sparsity: Removing less significant weights and exploiting sparsity in the model to speed up computation and reduce resource usage.

- Target-Specific Code Generation: The backend transforms the optimized IR into hardware-specific code. This involves generating low-level code for CPUs, GPUs, TPUs, or other accelerators.

- Kernel Fusion and Scheduling: Combining multiple small operations into a single kernel to reduce memory accesses and improve performance. Scheduling determines the order of operations to maximize hardware utilization and minimize latency.

- Memory Management: Efficiently allocating and managing memory resources, including handling tensor layouts and data movement between different memory hierarchies (e.g., CPU cache, GPU memory).

### Benefits of Machine Learning Compilers

- Performance Optimization: By generating highly optimized code tailored to the target hardware, MLCs can significantly improve the performance of machine learning models, often achieving orders of magnitude speedup.

- Portability: MLCs enable models to run efficiently on a wide range of hardware platforms without requiring manual optimization for each target.

- Scalability: They support efficient scaling of machine learning models to larger datasets and more complex architectures by optimizing resource usage.

- Reduced Development Time: Automating the optimization and code generation process reduces the need for manual tuning, accelerating the deployment of machine learning models.

### Challenges and Future Directions

- Hardware Diversity: The rapidly evolving landscape of specialized hardware accelerators poses a challenge for MLCs to keep up and provide optimal support for new architectures.

- Model Complexity: As machine learning models become more complex, with millions or even billions of parameters, efficiently optimizing and compiling these models becomes increasingly challenging.

- Dynamic Models: Supporting models that involve dynamic control flow (e.g., conditional statements, loops) adds complexity to the compilation process.

- Interoperability: Ensuring seamless interoperability between different machine learning frameworks and hardware targets remains a significant challenge.

### Conclusion

Machine Learning Compilers play a crucial role in the deployment of machine learning models, providing the necessary optimizations to leverage the full potential of modern hardware. By automating the transformation of high-level models into efficient executable code, MLCs enable scalable, portable, and high-performance machine learning applications across diverse hardware platforms. As the field continues to advance, addressing the challenges of hardware diversity, model complexity, and interoperability will be key to furthering the capabilities of machine learning compilers.

### Relevant links:

- MLIR: A Compiler Infrastructure for the End of Moore’s Law

- Mojo - Programming language for next generation compiler technology



## Next

ML Systems

MLIR: A Compiler Infrastructure for the End of Mooreâs Law

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/ai-memory-wall

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

AI & Memory Wall

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# AI & Memory Wall

Title and Authors:Title: AI and Memory WallAuthors: Amir Gholami, Zhewei Yao, Sehoon Kim, Coleman Hooper, Michael W. Mahoney, Kurt Keutzer

### Abstract Summary:

The paper discusses how the increase in model size and compute requirements for training and serving large language models (LLMs) has shifted the primary performance bottleneck to memory bandwidth. It analyzes the impact of this shift on transformer models and argues for redesigning model architecture, training, and deployment strategies to address memory limitations.

### Key Concepts:

- Memory bandwidth limitations

- Transformer models (encoder and decoder)

- Neural scaling laws

- Model architecture redesign

- Training and deployment strategies

- Arithmetic intensity

- Memory operations (MOPs)

### Problem Statement:

The main problem addressed in this paper is the growing disparity between the increasing compute requirements for AI models and the slower growth of memory and interconnect bandwidth, which has made memory the primary bottleneck in AI applications.

### Methods and Techniques:

- Arithmetic Intensity Analysis: Measures the number of FLOPs per byte loaded from memory to determine performance bottlenecks.

- Profiling Transformer Models: Analyzes the total FLOPs, MOPs, arithmetic intensity, and latency of BERT-Base, BERT-Large, and GPT-2 models to understand the impact of memory operations on model performance.

- Case Studies: Detailed examination of the runtime characteristics and performance bottlenecks associated with transformer inference, focusing on encoder and decoder architectures.

### Key Results:

- Profiling Results: GPT-2 exhibits significantly higher latency compared to BERT models due to higher memory operations and lower arithmetic intensity.

- Scaling Disparity: Peak server hardware FLOPS have scaled by 60,000× over the past 20 years, whereas DRAM and interconnect bandwidths have only scaled by 100× and 30×, respectively.

- Memory Wall: Memory bandwidth and intra/inter-chip memory transfers are becoming the main bottlenecks for large AI models, particularly in serving scenarios.

### Contributions and Innovations:

- Memory Bottleneck Identification: Highlights the critical issue of memory bandwidth as the primary bottleneck in AI applications.

- Redesign Proposals: Suggests redesigning AI model architectures, training, and deployment strategies to mitigate memory limitations.

- Efficient Training Algorithms: Discusses the need for more data-efficient training methods and optimization algorithms robust to low-precision training.

- Deployment Solutions: Proposes model compression techniques such as quantization and pruning to reduce the memory footprint and improve deployment efficiency.

### Future Work:

- Developing more data-efficient and memory-efficient training algorithms.

- Exploring new AI model architectures that are optimized for memory bandwidth constraints.

- Enhancing hardware designs to better balance compute and memory capabilities.

### Applications:

- AI Model Training: Improved training methods can lead to more efficient use of resources in developing large language models.

- Model Deployment: Enhanced deployment strategies, including model compression, can facilitate the use of large models in real-time applications.

- Hardware Design: Insights from the paper can guide the development of future AI accelerators with better memory bandwidth management.



## Next

ML Systems

FlashAttention-2

ML Systems

Efficient Memory Management for LLM Serving with PagedAttention

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/q

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

Quantization Technical Primer

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# Quantization Technical Primer



### Introduction

Quantization is a critical technique used to optimize large language models (LLMs) for deployment in resource-constrained environments. It involves reducing the precision of the numerical values that represent the model's parameters (weights) and activations. The primary goals of quantization are to decrease the model's memory footprint and computational requirements, while maintaining an acceptable level of accuracy.

### Types of Quantization

- Post-Training Quantization (PTQ)Static Quantization: Involves calibrating the model on a subset of the training data to determine the optimal quantization parameters. This method is effective but can be less accurate for complex models.Dynamic Quantization: Quantizes the model's weights after training but adjusts the activations dynamically during inference. This method is easier to implement and offers a good balance between performance and complexity.

- Static Quantization: Involves calibrating the model on a subset of the training data to determine the optimal quantization parameters. This method is effective but can be less accurate for complex models.

- Dynamic Quantization: Quantizes the model's weights after training but adjusts the activations dynamically during inference. This method is easier to implement and offers a good balance between performance and complexity.

- Quantization-Aware Training (QAT)During training, the model simulates the effects of quantization, allowing the weights to adjust and minimize the accuracy loss. QAT generally results in better performance compared to PTQ but requires more computational resources during training.

- During training, the model simulates the effects of quantization, allowing the weights to adjust and minimize the accuracy loss. QAT generally results in better performance compared to PTQ but requires more computational resources during training.

- Static Quantization: Involves calibrating the model on a subset of the training data to determine the optimal quantization parameters. This method is effective but can be less accurate for complex models.

- Dynamic Quantization: Quantizes the model's weights after training but adjusts the activations dynamically during inference. This method is easier to implement and offers a good balance between performance and complexity.

- During training, the model simulates the effects of quantization, allowing the weights to adjust and minimize the accuracy loss. QAT generally results in better performance compared to PTQ but requires more computational resources during training.

### Quantization Levels

- 8-bit Quantization (INT8)The most common form of quantization. It reduces the model size significantly while typically maintaining high accuracy.

- The most common form of quantization. It reduces the model size significantly while typically maintaining high accuracy.

- Lower-bit Quantization (INT4, INT2)Further reduces model size and increases inference speed but can lead to a more substantial accuracy drop. It is suitable for very resource-constrained environments.

- Further reduces model size and increases inference speed but can lead to a more substantial accuracy drop. It is suitable for very resource-constrained environments.

- Mixed Precision QuantizationUses a combination of different precisions (e.g., INT8 for weights and FP16 for activations) to balance accuracy and performance.

- Uses a combination of different precisions (e.g., INT8 for weights and FP16 for activations) to balance accuracy and performance.

- The most common form of quantization. It reduces the model size significantly while typically maintaining high accuracy.

- Further reduces model size and increases inference speed but can lead to a more substantial accuracy drop. It is suitable for very resource-constrained environments.

- Uses a combination of different precisions (e.g., INT8 for weights and FP16 for activations) to balance accuracy and performance.

### Quantization Techniques

- Uniform QuantizationThe range of values is divided into equal-sized intervals. This method is simple and efficient but might not capture the distribution of weights and activations optimally.

- The range of values is divided into equal-sized intervals. This method is simple and efficient but might not capture the distribution of weights and activations optimally.

- Non-Uniform QuantizationThe intervals are adjusted based on the distribution of values. Techniques like logarithmic quantization fall into this category, offering better accuracy at the cost of increased complexity.

- The intervals are adjusted based on the distribution of values. Techniques like logarithmic quantization fall into this category, offering better accuracy at the cost of increased complexity.

- The range of values is divided into equal-sized intervals. This method is simple and efficient but might not capture the distribution of weights and activations optimally.

- The intervals are adjusted based on the distribution of values. Techniques like logarithmic quantization fall into this category, offering better accuracy at the cost of increased complexity.

### Benefits of Quantization

- Reduced Memory Footprint: Quantized models require less memory, making them suitable for edge devices.

- Faster Inference: Lower precision arithmetic operations are faster, leading to reduced inference latency.

- Energy Efficiency: Quantized models consume less power, which is critical for battery-powered devices.

### Challenges and Considerations

- Accuracy Degradation: Quantization can lead to a drop in model accuracy. Techniques like QAT are essential to mitigate this issue.

- Hardware Support: Effective deployment of quantized models requires hardware that supports low-precision arithmetic operations.

- Compatibility: Ensuring compatibility with existing machine learning frameworks and inference engines is crucial for seamless deployment.

### Influential Papers on Quantization

- "Quantizing Deep Convolutional Networks for Efficient Inference: A Whitepaper" by Song Han, Huizi Mao, and William J. Dally (2015)This paper provides a comprehensive overview of quantization techniques and their application to deep convolutional networks, laying the groundwork for many subsequent advancements.Link

- This paper provides a comprehensive overview of quantization techniques and their application to deep convolutional networks, laying the groundwork for many subsequent advancements.

- Link

- "Deep Compression: Compressing Deep Neural Networks with Pruning, Trained Quantization and Huffman Coding" by Song Han, Huizi Mao, and William J. Dally (2015)Introduces a three-stage pipeline combining pruning, quantization, and Huffman coding to achieve significant compression and speedup.Link

- Introduces a three-stage pipeline combining pruning, quantization, and Huffman coding to achieve significant compression and speedup.

- Link

- "Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference" by Benoit Jacob et al. (2018)Discusses techniques for training neural networks to operate with integer arithmetic only, which is essential for efficient deployment on specialized hardware.Link

- Discusses techniques for training neural networks to operate with integer arithmetic only, which is essential for efficient deployment on specialized hardware.

- Link

- "Mixed Precision Training" by Paulius Micikevicius et al. (2017)Explores the use of mixed precision during training to achieve the benefits of quantization while maintaining high model accuracy.Link

- Explores the use of mixed precision during training to achieve the benefits of quantization while maintaining high model accuracy.

- Link

- "Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference" by Benoit Jacob et al. (2018)Details the practical aspects and benefits of using integer arithmetic for neural network inference.Link

- Details the practical aspects and benefits of using integer arithmetic for neural network inference.

- Link

- This paper provides a comprehensive overview of quantization techniques and their application to deep convolutional networks, laying the groundwork for many subsequent advancements.

- Link

- Introduces a three-stage pipeline combining pruning, quantization, and Huffman coding to achieve significant compression and speedup.

- Link

- Discusses techniques for training neural networks to operate with integer arithmetic only, which is essential for efficient deployment on specialized hardware.

- Link

- Explores the use of mixed precision during training to achieve the benefits of quantization while maintaining high model accuracy.

- Link

- Details the practical aspects and benefits of using integer arithmetic for neural network inference.

- Link

### Conclusion

Quantization is a powerful technique for optimizing large language models, enabling their deployment in resource-limited environments without significant sacrifices in performance. As the demand for efficient AI solutions grows, advancements in quantization methods will continue to play a pivotal role in the evolution of machine learning systems.

By leveraging the strategies and insights from influential research papers, practitioners can effectively apply quantization to enhance the efficiency and scalability of their models.

### Relevant Links:

- Quantization guide on MAX

## Next

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/mixtral-of-experts

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

Mixtral of Experts

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# Mixtral of Experts

### Title and Authors:

Title:Mixtral of Experts

Authors:Albert Q. Jiang, Alexandre Sablayrolles, Antoine Roux, Arthur Mensch, Blanche Savary, Chris Bamford, Devendra Singh Chaplot, Diego de las Casas, Emma Bou Hanna, Florian Bressand, Gianna Lengyel, Guillaume Bour, Guillaume Lample, Lélio Renard Lavaud, Lucile Saulnier, Marie-Anne Lachaux, Pierre Stock, Sandeep Subramanian, Sophia Yang, Szymon Antoniak, Teven Le Scao, Théophile Gervet, Thibaut Lavril, Thomas Wang, Timothée Lacroix, William El Sayed.

### Abstract Summary:

Mixtral 8x7B is a Sparse Mixture of Experts (SMoE) language model that uses 8 experts per layer, selecting 2 experts per token dynamically. This model outperforms or matches Llama 2 70B and GPT-3.5 across various benchmarks, especially excelling in mathematics, code generation, and multilingual tasks.

### Key Concepts:

- Sparse Mixture of Experts (SMoE)

- Router network for expert selection

- Multilingual pretraining

- Instruction fine-tuning

- Efficiency in parameter usage

- Comparative benchmarks with Llama 2 and GPT-3.5

- Reduced biases in language models

- Open-source model release under Apache 2.0 license

### Problem Statement:

The main problem addressed in this paper is improving the efficiency and performance of large language models by using a Sparse Mixture of Experts (SMoE) architecture to reduce computational costs while maintaining or surpassing the performance of existing models like Llama 2 70B and GPT-3.5.

### Methods and Techniques:

- Sparse Mixture of Experts (SMoE):Each layer in the model contains 8 experts. For every token, a router network selects 2 experts to process the token. This allows the model to utilize a subset of parameters, enhancing efficiency.

- Each layer in the model contains 8 experts. For every token, a router network selects 2 experts to process the token. This allows the model to utilize a subset of parameters, enhancing efficiency.

- Router Network:A gating mechanism that dynamically selects the top-K experts for each token using a softmax over the top-K logits of a linear layer.

- A gating mechanism that dynamically selects the top-K experts for each token using a softmax over the top-K logits of a linear layer.

- Multilingual Pretraining:Training the model on a large, diverse multilingual dataset with a context size of 32k tokens to improve performance across different languages.

- Training the model on a large, diverse multilingual dataset with a context size of 32k tokens to improve performance across different languages.

- Instruction Fine-Tuning:Using supervised fine-tuning and Direct Preference Optimization (DPO) to enhance the model's performance in following instructions.

- Using supervised fine-tuning and Direct Preference Optimization (DPO) to enhance the model's performance in following instructions.

- Efficient Inference with Megablocks:Utilizing specialized kernels and distributed processing techniques for efficient execution of the MoE layers.

- Utilizing specialized kernels and distributed processing techniques for efficient execution of the MoE layers.

- Each layer in the model contains 8 experts. For every token, a router network selects 2 experts to process the token. This allows the model to utilize a subset of parameters, enhancing efficiency.

- A gating mechanism that dynamically selects the top-K experts for each token using a softmax over the top-K logits of a linear layer.

- Training the model on a large, diverse multilingual dataset with a context size of 32k tokens to improve performance across different languages.

- Using supervised fine-tuning and Direct Preference Optimization (DPO) to enhance the model's performance in following instructions.

- Utilizing specialized kernels and distributed processing techniques for efficient execution of the MoE layers.

### Key Results:

- Performance Benchmarks:Mixtral 8x7B outperforms Llama 2 70B and GPT-3.5 on various tasks including mathematics, code generation, and multilingual understanding.Superior performance in specific benchmarks like GSM8K (mathematics) and MBPP (code generation).

- Mixtral 8x7B outperforms Llama 2 70B and GPT-3.5 on various tasks including mathematics, code generation, and multilingual understanding.

- Superior performance in specific benchmarks like GSM8K (mathematics) and MBPP (code generation).

- Efficiency:Uses 5x fewer active parameters than Llama 2 70B while achieving higher or similar performance.

- Uses 5x fewer active parameters than Llama 2 70B while achieving higher or similar performance.

- Bias Reduction:Demonstrates reduced biases and a more balanced sentiment profile in benchmarks such as BBQ and BOLD.

- Demonstrates reduced biases and a more balanced sentiment profile in benchmarks such as BBQ and BOLD.

- Mixtral 8x7B outperforms Llama 2 70B and GPT-3.5 on various tasks including mathematics, code generation, and multilingual understanding.

- Superior performance in specific benchmarks like GSM8K (mathematics) and MBPP (code generation).

- Uses 5x fewer active parameters than Llama 2 70B while achieving higher or similar performance.

- Demonstrates reduced biases and a more balanced sentiment profile in benchmarks such as BBQ and BOLD.

### Contributions and Innovations:

- Sparse Mixture of Experts Architecture:Efficient use of parameters by dynamically selecting a subset of experts for each token.

- Efficient use of parameters by dynamically selecting a subset of experts for each token.

- Instruction-Tuned Model:Fine-tuned variant (Mixtral 8x7B – Instruct) that surpasses GPT-3.5 Turbo and other models in human benchmarks.

- Fine-tuned variant (Mixtral 8x7B – Instruct) that surpasses GPT-3.5 Turbo and other models in human benchmarks.

- Open-Source Release:Both base and instruct models are released under the Apache 2.0 license, promoting accessibility and further research.

- Both base and instruct models are released under the Apache 2.0 license, promoting accessibility and further research.

- Efficient use of parameters by dynamically selecting a subset of experts for each token.

- Fine-tuned variant (Mixtral 8x7B – Instruct) that surpasses GPT-3.5 Turbo and other models in human benchmarks.

- Both base and instruct models are released under the Apache 2.0 license, promoting accessibility and further research.

### Future Work:

The authors suggest further exploration in the following areas:

- Enhancing the routing mechanism to improve load balancing across experts.

- Investigating the impact of different expert selection strategies.

- Extending the model's capabilities to other domains and tasks.

### Applications:

- Natural Language Understanding:Enhanced performance in tasks requiring comprehension of large contexts and diverse languages.

- Enhanced performance in tasks requiring comprehension of large contexts and diverse languages.

- Code Generation:Superior results in programming tasks, making it useful for automated code synthesis and completion.

- Superior results in programming tasks, making it useful for automated code synthesis and completion.

- Mathematics:High accuracy in solving complex mathematical problems, applicable in educational tools and scientific research.

- High accuracy in solving complex mathematical problems, applicable in educational tools and scientific research.

- Enhanced performance in tasks requiring comprehension of large contexts and diverse languages.

- Superior results in programming tasks, making it useful for automated code synthesis and completion.

- High accuracy in solving complex mathematical problems, applicable in educational tools and scientific research.

### Relevant Links:

- Code Repository

- Webpage

- LMSys Leaderboard

## Next

Models

Mistral-7B

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/llama

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

Llama 2

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# Llama 2

Title and Authors:

- Title: "Llama 2: Open Foundation and Fine-Tuned Chat Models"

- Authors: Hugo Touvron, Louis Martin, Kevin Stone, Peter Albert, Amjad Almahairi, Yasmine Babaei, Nikolay Bashlykov, Soumya Batra, Prajjwal Bhargava, Shruti Bhosale, Dan Bikel, Lukas Blecher, Cristian Canton Ferrer, Moya Chen, Guillem Cucurull, David Esiobu, Jude Fernandes, Jeremy Fu, Wenyin Fu, Brian Fuller, Cynthia Gao, Vedanuj Goswami, Naman Goyal, Anthony Hartshorn, Saghar Hosseini, Rui Hou, Hakan Inan, Marcin Kardas, Viktor Kerkez, Madian Khabsa, Isabel Kloumann, Artem Korenev, Punit Singh Koura, Marie-Anne Lachaux, Thibaut Lavril, Jenya Lee, Diana Liskovich, Yinghai Lu, Yuning Mao, Xavier Martinet, Todor Mihaylov, Pushkar Mishra, Igor Molybog, Yixin Nie, Andrew Poulton, Jeremy Reizenstein, Rashi Rungta, Kalyan Saladi, Alan Schelten, Ruan Silva, Eric Michael Smith, Ranjan Subramanian, Xiaoqing Ellen Tan, Binh Tang, Ross Taylor, Adina Williams, Jian Xiang Kuan, Puxin Xu, Zheng Yan, Iliyan Zarov, Yuchen Zhang, Angela Fan, Melanie Kambadur, Sharan Narang, Aurelien Rodriguez, Robert Stojnic, Sergey Edunov, Thomas Scialom.

Abstract Summary:

- The paper introduces Llama 2, a collection of pretrained and fine-tuned large language models ranging from 7 billion to 70 billion parameters, optimized for dialogue use cases. Llama 2-Chat models outperform existing open-source models on most benchmarks and offer a viable alternative to some closed-source models, with detailed fine-tuning and safety improvements shared to encourage responsible development of large language models.

Key Concepts:

- Large Language Models (LLMs)

- Pretrained and fine-tuned models

- Llama 2-Chat

- Dialogue optimization

- Safety and helpfulness benchmarks

- Reinforcement Learning with Human Feedback (RLHF)

- Supervised Fine-Tuning (SFT)

- Model safety and evaluation

Problem Statement:

- The main problem addressed by the paper is the development of open, high-performing language models that can serve as viable alternatives to closed-source models, particularly optimized for dialogue use cases while ensuring safety and helpfulness.

Methods and Techniques:

- Pretraining:Utilized a large corpus of publicly available data, excluding data from Meta’s products and services, with robust data cleaning and increased context length.Applied the AdamW optimizer, cosine learning rate schedule, and techniques like grouped-query attention for better scalability.

- Utilized a large corpus of publicly available data, excluding data from Meta’s products and services, with robust data cleaning and increased context length.

- Applied the AdamW optimizer, cosine learning rate schedule, and techniques like grouped-query attention for better scalability.

- Supervised Fine-Tuning (SFT):Collected high-quality instruction tuning data focusing on dialogue-style instructions.Fine-tuned using a special token to separate prompt and answer segments, with a cosine learning rate schedule and weight decay.

- Collected high-quality instruction tuning data focusing on dialogue-style instructions.

- Fine-tuned using a special token to separate prompt and answer segments, with a cosine learning rate schedule and weight decay.

- Reinforcement Learning with Human Feedback (RLHF):Collected human preference data using a binary comparison protocol.Trained two separate reward models for helpfulness and safety.Employed Proximal Policy Optimization (PPO) and Rejection Sampling for iterative fine-tuning, with safety checks and margin components in the loss function to improve alignment.

- Collected human preference data using a binary comparison protocol.

- Trained two separate reward models for helpfulness and safety.

- Employed Proximal Policy Optimization (PPO) and Rejection Sampling for iterative fine-tuning, with safety checks and margin components in the loss function to improve alignment.

- Ghost Attention (GAtt):Introduced a technique to maintain instruction consistency over multiple turns in dialogue by synthetically concatenating instructions to user messages.

- Introduced a technique to maintain instruction consistency over multiple turns in dialogue by synthetically concatenating instructions to user messages.

- Utilized a large corpus of publicly available data, excluding data from Meta’s products and services, with robust data cleaning and increased context length.

- Applied the AdamW optimizer, cosine learning rate schedule, and techniques like grouped-query attention for better scalability.

- Collected high-quality instruction tuning data focusing on dialogue-style instructions.

- Fine-tuned using a special token to separate prompt and answer segments, with a cosine learning rate schedule and weight decay.

- Collected human preference data using a binary comparison protocol.

- Trained two separate reward models for helpfulness and safety.

- Employed Proximal Policy Optimization (PPO) and Rejection Sampling for iterative fine-tuning, with safety checks and margin components in the loss function to improve alignment.

- Introduced a technique to maintain instruction consistency over multiple turns in dialogue by synthetically concatenating instructions to user messages.

Key Results:

- Llama 2-Chat models generally perform better than existing open-source models and are on par with some closed-source models on various benchmarks.

- The models demonstrated significant improvements in safety and helpfulness through iterative fine-tuning and reward modeling.

- Human evaluations showed that Llama 2-Chat outperformed ChatGPT in both safety and helpfulness.

Contributions and Innovations:

- Development and open release of Llama 2, a family of pretrained and fine-tuned LLMs, with detailed methodology to improve model safety and performance.

- Introduction of Ghost Attention (GAtt) to enhance multi-turn dialogue consistency.

- Iterative application of RLHF combining PPO and Rejection Sampling for better alignment with human preferences.

- Comprehensive evaluation framework and release of models for research and commercial use, fostering community collaboration.

Future Work:

- The authors suggest further research on fine-tuning techniques, handling instruction consistency in dialogues, and exploring additional methods to enhance model safety and alignment with human preferences.

Applications:

- Customer service chatbots

- Virtual assistants

- Interactive educational tools

- Content generation and summarization

- Automated technical support

Relevant Links:

- Meta AI resources for Llama

- Llama responsible use guide

- Llama GitHub repository

- MAX end to end llama pipelines example



## Next

Models

Mistral-7B

Models

Large Language Model Technical Primer

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/byte-pair-encoding

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

Byte Pair Encoding (BPE)

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# Byte Pair Encoding (BPE)

### Title and Authors:

Title: Neural Machine Translation of Rare Words with Subword Units

Authors: Rico Sennrich, Barry Haddow, Alexandra Birch

### Abstract Summary:

This paper introduces an approach for neural machine translation (NMT) that enables open-vocabulary translation by encoding rare and unknown words as sequences of subword units. This method, which uses techniques like byte pair encoding (BPE), outperforms traditional back-off dictionary methods for the WMT 2015 English→German and English→Russian translation tasks.

### Key Concepts:

- Neural Machine Translation (NMT)

- Open-vocabulary translation

- Subword units

- Byte Pair Encoding (BPE)

- Rare word translation

- Character-level models

- Segmentation techniques

- BLEU score

- CHRF3 score

### Problem Statement:

The main problem addressed in this paper is the translation of rare and out-of-vocabulary words in NMT models, which traditionally rely on fixed vocabularies and back-off dictionaries, limiting their effectiveness.

### Methods and Techniques:

- Neural Machine Translation (NMT) Architecture: The study uses an encoder-decoder network with recurrent neural networks (RNNs). The encoder is a bidirectional RNN with gated recurrent units (GRUs) that processes the input sequence. The decoder predicts the target sequence, using a context vector computed from the encoder's annotations through an alignment model.

- Subword Units: The paper explores the use of subword units to encode rare and unknown words. This involves segmenting words into smaller units, such as morphemes or phonemes, that can be translated more effectively than whole words.

- Byte Pair Encoding (BPE): BPE is adapted for word segmentation by iteratively merging the most frequent pairs of characters in the vocabulary. This results in a fixed-size vocabulary of variable-length character sequences that can represent an open vocabulary.

### Key Results:

- BLEU Scores: Subword models improved BLEU scores over baseline models for the WMT 2015 tasks, with increases of up to 1.1 for English→German and 1.3 for English→Russian.

- CHRF3 Scores: Character n-gram F3 scores also showed improvements, correlating well with human judgment.

- Unigram F1 Scores: The subword models achieved better accuracy for rare and unseen words compared to large-vocabulary models and back-off dictionaries.

### Contributions and Innovations:

- Open-Vocabulary NMT: Demonstrated that NMT can handle open-vocabulary translation by using subword units, simplifying the translation process and improving accuracy.

- BPE for Word Segmentation: Adapted BPE for segmenting words into subword units, creating a compact and efficient representation for neural networks.

- Translation Quality: Improved translation quality for rare and unseen words without relying on extensive dictionaries.

### Future Work:

The authors suggest exploring:

- Automatic learning of optimal vocabulary size for different language pairs and training data amounts.

- Developing bilingual segmentation algorithms that can improve the alignment of subword units across languages.

### Applications:

- Machine Translation Systems: Can be used in developing more robust and accurate machine translation systems for languages with complex morphology or those with limited resources.

- Text Generation: Useful in applications requiring open-vocabulary text generation, such as chatbots or automated content creation.

- Speech Recognition: Potentially beneficial for speech-to-text systems by improving the handling of rare and novel words.

### Relevant Links:

- ArXiv Paper

## Next

ML Systems

Rotary Position Embedding (RoPE)

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/flash-attenti

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

FlashAttention

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# FlashAttention

### Title and Authors

Title: FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness

Authors: Tri Dao, Daniel Y. Fu, Stefano Ermon, Atri Rudra, and Christopher Ré

### Abstract Summary

Transformers are computationally expensive and memory-intensive for long sequences due to the quadratic complexity of self-attention. FlashAttention is an IO-aware exact attention algorithm that uses tiling to minimize memory reads/writes, resulting in significant speedup and reduced memory usage without sacrificing model quality.

### Key Concepts

- Transformers

- Self-Attention

- IO-Aware Algorithms

- Tiling

- GPU Memory Hierarchy

- FlashAttention

- Block-Sparse Attention

- Model Training Speedup

- Memory Efficiency

- Long-Range Arena Benchmark

### Problem Statement

The main problem addressed by this paper is the inefficiency of standard self-attention mechanisms in Transformers due to their quadratic time and memory complexity, particularly for long sequences.

### Methods and Techniques

- Tiling: The attention computation is restructured to split the input into blocks, allowing incremental computation of the softmax reduction. This reduces the number of memory accesses.

- Recomputation: Instead of storing large intermediate matrices, the softmax normalization factor from the forward pass is stored to recompute attention on-chip during the backward pass.

- CUDA Implementation: Fine-grained control over memory access is achieved by implementing FlashAttention in CUDA and fusing all attention operations into one GPU kernel.

### Key Results

- FlashAttention reduces the number of high bandwidth memory (HBM) accesses compared to standard attention, resulting in up to 7.6× speedup on GPT-2.

- FlashAttention provides a 15% speedup on BERT-large training compared to the MLPerf 1.1 training speed record, and 3× speedup on GPT-2.

- It enables longer context in Transformers, yielding higher quality models, such as a 0.7 better perplexity on GPT-2 and a 6.4-point lift on long-document classification.

- FlashAttention enables the first Transformers to achieve better-than-chance performance on Path-X (16K sequence length) and Path-256 (64K sequence length).

### Contributions and Innovations

- IO-Awareness: The introduction of IO-aware principles to reduce memory accesses, significantly improving efficiency.

- Exact and Approximate Attention: FlashAttention is extended to block-sparse attention, providing faster approximate attention.

- Open-Source Implementation: FlashAttention is implemented in CUDA and open-sourced, making it easier for others to build on this work.

### Future Work

The authors suggest:

- Extending the IO-aware approach to other deep learning modules beyond attention.

- Developing methods for writing attention algorithms in high-level languages that can compile to IO-aware implementations.

- Exploring multi-GPU implementations for parallelizing attention computations across multiple GPUs.

### Applications

- Natural Language Processing (NLP): Faster and more memory-efficient training of large language models like BERT and GPT-2.

- Image Classification: Improved attention mechanisms in Transformer-based image classification models.

- Long-Document Processing: Enhanced performance in tasks requiring long-context understanding, such as legal document analysis and medical report classification.

### Relevant Links

- FlashAttention code on GitHub

## Next

ML Systems

FlashAttention-2

ML Systems

Efficient Memory Management for LLM Serving with PagedAttention

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/flashattention-2

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

FlashAttention-2

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# FlashAttention-2

### Title and Authors:

Title: FlashAttention-2: Faster Attention with Better Parallelism and Work Partitioning

Authors: Tri Dao (Department of Computer Science, Princeton University; Department of Computer Science, Stanford University)

### Abstract Summary:

FlashAttention-2 improves upon FlashAttention by optimizing work partitioning and parallelism to significantly enhance efficiency and speed. The new algorithm achieves approximately 2× speedup over FlashAttention, reaching 50-73% of the theoretical maximum FLOPs/s on A100 GPUs, and enables faster end-to-end training of GPT-style models.

### Key Concepts:

- Scaling Transformers

- Attention Layer Bottleneck

- FlashAttention

- GPU Memory Hierarchy

- Work Partitioning

- Algorithm Optimization

- Parallelism in GPUs

- Training Speed of GPT-style Models

### Problem Statement:

The main problem addressed by this paper is the inefficiency in the attention layer of Transformers when scaling to longer sequence lengths, which results in high memory usage and slow runtime.

### Methods and Techniques:

- Algorithm Tweaks:Reducing Non-Matmul FLOPs: Optimizing the algorithm to minimize non-matmul operations which are slower on GPUs.Parallelizing Attention Computation: Distributing the computation across different thread blocks to increase GPU occupancy.

- Reducing Non-Matmul FLOPs: Optimizing the algorithm to minimize non-matmul operations which are slower on GPUs.

- Parallelizing Attention Computation: Distributing the computation across different thread blocks to increase GPU occupancy.

- Work Partitioning:Forward Pass: Parallelizing both forward and backward passes along the sequence length dimension.Backward Pass: Using atomic adds to manage updates across different thread blocks.

- Forward Pass: Parallelizing both forward and backward passes along the sequence length dimension.

- Backward Pass: Using atomic adds to manage updates across different thread blocks.

- Work Partitioning Between Warps:Splitting the workload within each thread block to reduce shared memory access and improve efficiency.

- Splitting the workload within each thread block to reduce shared memory access and improve efficiency.

- Reducing Non-Matmul FLOPs: Optimizing the algorithm to minimize non-matmul operations which are slower on GPUs.

- Parallelizing Attention Computation: Distributing the computation across different thread blocks to increase GPU occupancy.

- Forward Pass: Parallelizing both forward and backward passes along the sequence length dimension.

- Backward Pass: Using atomic adds to manage updates across different thread blocks.

- Splitting the workload within each thread block to reduce shared memory access and improve efficiency.

### Key Results:

FlashAttention-2 achieves:

- 2× speedup compared to FlashAttention.

- 1.3-2.5× speedup compared to FlashAttention in Triton.

- Up to 10× faster than standard attention implementation.

- Training speed up to 225 TFLOPs/s per A100 GPU (72% model FLOPs utilization).

### Contributions and Innovations:

- Algorithmic Improvements: Significant reduction in non-matmul FLOPs and better parallelization.

- Parallelism and Work Partitioning: Enhanced GPU resource utilization through optimized work distribution across thread blocks and warps.

- Empirical Validation: Demonstrated substantial speedup and efficiency improvements in attention computations and end-to-end model training.

### Future Work:

- Optimize FlashAttention-2 for new hardware features on H100 GPUs.

- Extend the applicability to other devices like AMD GPUs and new data types such as FP8.

- Collaborate with compiler researchers to make these optimization techniques easily programmable.

### Applications:

- Language Modeling: Training models with longer context lengths for better understanding of books and long-form content.

- High-Resolution Image Understanding: Enabling models to process and analyze high-resolution images more efficiently.

- Audio and Video Generation: Improving performance in applications involving long sequences of audio and video data.

- Long Document Querying: Enhancing capabilities of models to handle and query long documents.

### Relevant Links:

- FlashAttention-2 GitHub Repository

- Triton Implementation

- xformers Library



## Next

ML Systems

FlashAttention

ML Systems

AI & Memory Wall

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/mistral

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

Mistral-7B

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# Mistral-7B

## Title and Authors

The title of the paper is "Mistral 7B". The authors include Albert Q. Jiang, Alexandre Sablayrolles, Arthur Mensch, Chris Bamford, Devendra Singh Chaplot, Diego de las Casas, Florian Bressand, Gianna Lengyel, Guillaume Lample, Lucile Saulnier, Lélio Renard Lavaud, Marie-Anne Lachaux, Pierre Stock, Teven Le Scao, Thibaut Lavril, Thomas Wang, Timothée Lacroix, and William El Sayed.

## Abstract Summary

Mistral 7B is a 7-billion-parameter language model noted for its superior performance and efficiency, outperforming existing models like Llama 2 (13B) and Llama 1 (34B) across various benchmarks. It incorporates innovative attention mechanisms, such as grouped-query and sliding window attention, to enhance inference speed and manage long sequences effectively.

## Key Concepts

- Grouped-query attention (GQA)

- Sliding window attention (SWA)

- Fine-tuning for instruction following

- Model efficiency and performance metrics

- Application in real-world scenarios

- Integration with cloud platforms and Hugging Face

Problem Statement:The main challenge addressed by the paper is the need for high-performance language models that maintain efficiency and reduced computational costs, particularly important for deployment in real-world applications.

## Methods and Techniques

- Grouped-query attention (GQA): Accelerates inference speed and reduces memory requirements during decoding, enabling higher throughput.

- Sliding window attention (SWA): Manages longer sequences effectively at a lower computational cost, helping to alleviate common limitations in large language models.

## Key Results

Mistral 7B surpasses Llama 2 13B and Llama 1 34B in all benchmarks, especially in code generation, mathematics, and reasoning tasks. It approaches the coding performance of Code-Llama 7B without compromising on non-code related benchmarks.

## Contributions and Innovations

The paper introduces significant advancements in attention mechanisms, demonstrating that Mistral 7B can achieve high performance while maintaining efficient inference. This is particularly relevant for ML engineers looking to implement efficient, high-performance models in production environments.

## Future Work

The authors suggest exploring further improvements in model performance and efficiency, possibly through more innovative attention mechanisms or architectural tweaks.

## Applications

Mistral 7B's adaptability makes it suitable for a wide range of applications, including real-time systems on cloud platforms, integration with AI platforms like Hugging Face, and fine-tuning for specific tasks such as chat models and instruction following.

## Relevant Links

- Mistral 7B Source Code: Mistral Source Code

- Mistral 7B Announcement Page: Mistral Announcement

- SkyPilot: SkyPilot

- Hugging Face Repository: Mistral on Hugging Face

- xFormers: xFormers Library

These links provide access to the model's codebase, additional details about the model, and integration tools for deploying and using the model.

## Next

Models

Llama 2

Models

Mixtral of Experts

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/p

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

Phi-3-mini

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# Phi-3-mini

### Title and Authors:

The title of the paper is "Phi-3 Technical Report: A Highly Capable Language Model Locally on Your Phone". The authors are a large team from Microsoft including Marah Abdin, Russell J. Hewett, Olatunji Ruwase, Sam Ade Jacobs, Jamie Huynh, and many others, totaling over fifty contributors.

### Abstract Summary:

The paper introduces phi-3-mini, a compact 3.8 billion parameter language model capable of running on mobile devices with performance comparable to larger models such as GPT-3.5 and Mixtral 8x7B. It emphasizes the use of a unique dataset composed of heavily filtered web data and synthetic data to train smaller models without compromising their performance.

### Key Concepts:

- Small Language Models (SLMs): Efficient models capable of deployment on devices with limited resources.

- Dataset Optimization: Use of heavily filtered web data and synthetic data for training to enhance model performance.

- Model Scaling: Detailed scaling results for models with different parameters (phi-3-mini, phi-3-small, phi-3-medium) showing effectiveness at various scales.

- Quantization: Techniques to reduce the model size for mobile deployment, specifically 4-bit quantization for phi-3-mini.

### Problem Statement:

The main challenge addressed by the paper is developing a language model that is both small enough to operate on a mobile phone and powerful enough to perform at the level of much larger contemporary models.

### Methods and Techniques:

- Transformer Architecture: Utilizing a transformer decoder architecture with modifications for size and performance optimization.

- Quantization: Applying 4-bit quantization to the model to fit and perform efficiently on mobile devices.

- LongRope: A technique to extend the context length in the smaller model version, enabling it to handle longer text sequences effectively.

- Data Filtering: Innovations in selecting and processing training data to maximize model effectiveness without the need for extensive computing resources.

### Key Results:

Phi-3-mini demonstrated strong performance across various benchmarks, achieving scores like 69% on MMLU and 8.38 on MT-bench. It rivals larger models and showcases the effectiveness of its training and architecture in a mobile-friendly format.

### Contributions and Innovations:

- Model Size Reduction: Successfully reducing the model size to enable local deployment on mobile devices without losing performance.

- Data Filtering and Synthetic Data Use: Innovations in data preparation that allow smaller models to perform as well as larger ones.

- Model Architectural Adjustments: Implementing architectural techniques like LongRope and quantization to maintain performance within the constraints of mobile hardware.

### Future Work:

The authors suggest further optimization of their data mixture for larger models and continued investigation into reducing the model size while maintaining or improving performance benchmarks.

### Applications:

The phi-3-mini can be used in mobile applications requiring natural language processing, such as virtual assistants, mobile-based chatbots, and real-time language translation applications that can operate fully offline.

### Relevant Links

Here are the relevant links extracted from the paper:

- Preprints and Research Publications:Gunasekar, Suriya, et al. "Textbooks Are All You Need." arXiv preprint arXiv:2306.11644, 2023.Vaswani, Ashish, et al. "Attention Is All You Need." Advances in Neural Information Processing Systems, vol. 30, 2017.Kaplan, Jared, et al. "Scaling Laws for Neural Language Models." arXiv preprint arXiv:2001.08361, 2020.Ding, Yiran, et al. "Longrope: Extending LLM Context Window Beyond 2 Million Tokens." arXiv preprint arXiv:2409.05463, 2024.Other various arXiv preprints cited throughout the paper related to language models and their training methods.

- Gunasekar, Suriya, et al. "Textbooks Are All You Need." arXiv preprint arXiv:2306.11644, 2023.

- Vaswani, Ashish, et al. "Attention Is All You Need." Advances in Neural Information Processing Systems, vol. 30, 2017.

- Kaplan, Jared, et al. "Scaling Laws for Neural Language Models." arXiv preprint arXiv:2001.08361, 2020.

- Ding, Yiran, et al. "Longrope: Extending LLM Context Window Beyond 2 Million Tokens." arXiv preprint arXiv:2409.05463, 2024.

- Other various arXiv preprints cited throughout the paper related to language models and their training methods.

- Benchmarks and Datasets:Hendrycks, Dan, et al. "Measuring Mathematical Problem Solving With the MATH Dataset." 2021.Zellers, Rowan, et al. "HellaSwag: Can a Machine Really Finish Your Sentence?" ACL 2019.Clark, Peter, et al. "Think You Have Solved Question Answering? Try ARC, The AI2 Reasoning Challenge." 2018.Other benchmarks like GSM-8K, MedQA, AGIEval, TriviaQA, Arc-C, Arc-E, PIQA, SociQA, BigBench-Hard, WinoGrande, OpenBookQA, BoolQ, CommonsenseQA, TruthfulQA, and HumanEval mentioned for model evaluation.

- Hendrycks, Dan, et al. "Measuring Mathematical Problem Solving With the MATH Dataset." 2021.

- Zellers, Rowan, et al. "HellaSwag: Can a Machine Really Finish Your Sentence?" ACL 2019.

- Clark, Peter, et al. "Think You Have Solved Question Answering? Try ARC, The AI2 Reasoning Challenge." 2018.

- Other benchmarks like GSM-8K, MedQA, AGIEval, TriviaQA, Arc-C, Arc-E, PIQA, SociQA, BigBench-Hard, WinoGrande, OpenBookQA, BoolQ, CommonsenseQA, TruthfulQA, and HumanEval mentioned for model evaluation.

- Organizations and Projects:Meta AI's Llama-3 announcement.Various references to OpenAI's GPT models and their blogs.

- Meta AI's Llama-3 announcement.

- Various references to OpenAI's GPT models and their blogs.

- Gunasekar, Suriya, et al. "Textbooks Are All You Need." arXiv preprint arXiv:2306.11644, 2023.

- Vaswani, Ashish, et al. "Attention Is All You Need." Advances in Neural Information Processing Systems, vol. 30, 2017.

- Kaplan, Jared, et al. "Scaling Laws for Neural Language Models." arXiv preprint arXiv:2001.08361, 2020.

- Ding, Yiran, et al. "Longrope: Extending LLM Context Window Beyond 2 Million Tokens." arXiv preprint arXiv:2409.05463, 2024.

- Other various arXiv preprints cited throughout the paper related to language models and their training methods.

- Hendrycks, Dan, et al. "Measuring Mathematical Problem Solving With the MATH Dataset." 2021.

- Zellers, Rowan, et al. "HellaSwag: Can a Machine Really Finish Your Sentence?" ACL 2019.

- Clark, Peter, et al. "Think You Have Solved Question Answering? Try ARC, The AI2 Reasoning Challenge." 2018.

- Other benchmarks like GSM-8K, MedQA, AGIEval, TriviaQA, Arc-C, Arc-E, PIQA, SociQA, BigBench-Hard, WinoGrande, OpenBookQA, BoolQ, CommonsenseQA, TruthfulQA, and HumanEval mentioned for model evaluation.

- Meta AI's Llama-3 announcement.

- Various references to OpenAI's GPT models and their blogs.



## Next

Models

Gemma: Open Models Based on Gemini Research and Technology

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/gemma-open-models-based-on-gemini-research-and-technology

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

Gemma: Open Models Based on Gemini Research and Technology

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# Gemma: Open Models Based on Gemini Research and Technology

## Title and Authors

- Title: "Gemma: Open Models Based on Gemini Research and Technology"

- Authors: Gemma Team, Google DeepMind

## Abstract Summary

The paper introduces Gemma, a series of lightweight, state-of-the-art open models derived from the Gemini models. These models showcase robust performance across various benchmarks in language understanding, reasoning, and safety, highlighting the critical role of responsible large language model (LLM) releases for safety and innovation.

## Key Concepts

- Large Language Models (LLMs)

- Model scaling (2 billion and 7 billion parameters)

- Performance evaluation across multiple benchmarks

- Safety and responsibility in AI

- Open-source availability for research and development

## Problem Statement

The main problem addressed is developing efficient, scalable, and safe large language models that can be openly accessed for further research and application development, focusing on improving safety standards and innovation in the field of LLMs.

## Methods and Techniques

Methods and Techniques:

- Data Training: Trained on up to 6 trillion tokens using techniques inspired by the Gemini model family.

- Architectural Innovations: Implementation of improvements like Multi-Query Attention, RoPE Embeddings, and GeGLU Activations.

- Fine-Tuning: Both models were fine-tuned for dialogue, instruction-following, helpfulness, and safety.

- Evaluation: Comprehensive benchmarks for quantitative and qualitative analysis, including performance comparisons with other models.

## Key Results

- Gemma models outperform similarly sized models in 11 out of 18 text-based tasks.

- Demonstrated advancements in safety and responsibility in model deployment.

- Provided extensive evaluations and comparative performance metrics (see Fig. 1 in the document for a visual comparison).

## Benchmark Overview

The paper provides detailed comparative performance benchmarks of the Gemma models against other models. Here are some key findings from the benchmark evaluations included in the document:

### Models compared with

- LLaMA 2 (7B and 13B)

- Mistral (7B)

- Gemma (2B and 7B)

### Key Benchmarks and Results

- MMLU (Mathematics and Science):Gemma 7B achieves 64.3% top-1 accuracy in 5-shot settings, outperforming LLaMA 2 (7B at 45.3% and 13B at 54.8%) and Mistral (7B at 62.5%).This demonstrates Gemma's superior capabilities in handling complex reasoning tasks in mathematics and science.

- Gemma 7B achieves 64.3% top-1 accuracy in 5-shot settings, outperforming LLaMA 2 (7B at 45.3% and 13B at 54.8%) and Mistral (7B at 62.5%).

- This demonstrates Gemma's superior capabilities in handling complex reasoning tasks in mathematics and science.

- HellaSwag (Contextual Commonsense Reasoning):Gemma 7B scores 81.2% in 0-shot settings, matching Mistral and slightly outperforming LLaMA 2 13B (80.7%).

- Gemma 7B scores 81.2% in 0-shot settings, matching Mistral and slightly outperforming LLaMA 2 13B (80.7%).

- PIQA (Physical Interaction Question Answering):Again, Gemma 7B scores highly with 81.2%, indicating strong performance in physical reasoning.

- Again, Gemma 7B scores highly with 81.2%, indicating strong performance in physical reasoning.

- Winogrande (Commonsense Reasoning):Gemma 7B scores 72.3% in partial scoring settings, showing competitive performance in commonsense reasoning.

- Gemma 7B scores 72.3% in partial scoring settings, showing competitive performance in commonsense reasoning.

- Code-related benchmarks (e.g., HumanEval, MBPP):Gemma 7B excels in code synthesis and problem-solving, with scores like 32.3% pass@1 in HumanEval and 44.4% in 3-shot MBPP, significantly higher than the competitors.

- Gemma 7B excels in code synthesis and problem-solving, with scores like 32.3% pass@1 in HumanEval and 44.4% in 3-shot MBPP, significantly higher than the competitors.

- Miscellaneous Tasks:In other tasks such as SIQA, Boolq, and ARC (question answering), Gemma generally matches or exceeds performance benchmarks set by similarly sized models.

- In other tasks such as SIQA, Boolq, and ARC (question answering), Gemma generally matches or exceeds performance benchmarks set by similarly sized models.

- Gemma 7B achieves 64.3% top-1 accuracy in 5-shot settings, outperforming LLaMA 2 (7B at 45.3% and 13B at 54.8%) and Mistral (7B at 62.5%).

- This demonstrates Gemma's superior capabilities in handling complex reasoning tasks in mathematics and science.

- Gemma 7B scores 81.2% in 0-shot settings, matching Mistral and slightly outperforming LLaMA 2 13B (80.7%).

- Again, Gemma 7B scores highly with 81.2%, indicating strong performance in physical reasoning.

- Gemma 7B scores 72.3% in partial scoring settings, showing competitive performance in commonsense reasoning.

- Gemma 7B excels in code synthesis and problem-solving, with scores like 32.3% pass@1 in HumanEval and 44.4% in 3-shot MBPP, significantly higher than the competitors.

- In other tasks such as SIQA, Boolq, and ARC (question answering), Gemma generally matches or exceeds performance benchmarks set by similarly sized models.



These benchmark comparisons emphasize Gemma's overall robustness and versatility across a wide range of tasks, showcasing its state-of-the-art performance, especially in domains requiring advanced understanding and reasoning.

## Contributions and Innovations

- Development of a new family of open models scalable for various applications.

- Introduction of architectural and training enhancements for better performance and efficiency.

- Emphasis on responsible deployment and safety of LLMs, aiming to set a standard for future model releases.

## Future Work:

The authors suggest ongoing improvements in model safety, training efficiency, and application-specific tuning. They also highlight the need for continued research into the impact of instruction tuning regimes and model development methodologies.

## Applications

Gemma models are suitable for various applications, including automated customer support, content generation, language translation, and more complex tasks like coding and scientific analysis, due to their enhanced understanding and reasoning capabilities.

## Relevant Links

- XLA - Optimizing Compiler for TensorFlowXLA: TensorFlow

- XLA: TensorFlow

- GSPMD: General and Scalable Parallelization for ML Computation GraphsGSPMD: arXiv

- GSPMD: arXiv

- RoFormer: Enhanced Transformer with Rotary Position EmbeddingRoFormer: arXiv

- RoFormer: arXiv

- Preventing Verbatim Memorization in Language Models Gives a False Sense of PrivacyVerbatim Memorization: arXiv

- Verbatim Memorization: arXiv

- Palm 2 Technical ReportPalm 2: arXiv

- Palm 2: arXiv

- Program Synthesis with Large Language ModelsProgram Synthesis: arXiv

- Program Synthesis: arXiv

- PIQA: Reasoning about Physical Commonsense in Natural LanguagePIQA: arXiv

- PIQA: arXiv

- The LAMBADA dataset: Word prediction requiring a broad discourse contextLAMBADA: arXiv

- LAMBADA: arXiv

- Sequence to Sequence Learning with Neural NetworksSequence Learning: arXiv

- Sequence Learning: arXiv

- Attention is All You NeedAttention Mechanism: arXiv

- Attention Mechanism: arXiv

- BERT: Pre-training of Deep Bidirectional Transformers for Language UnderstandingBERT: arXiv

- BERT: arXiv

- Root Mean Square Layer NormalizationRMSNorm: arXiv

- RMSNorm: arXiv

- Ethical and Social Risks of Harm from Language ModelsEthical Risks: arXiv

- Ethical Risks: arXiv

- XLA: TensorFlow

- GSPMD: arXiv

- RoFormer: arXiv

- Verbatim Memorization: arXiv

- Palm 2: arXiv

- Program Synthesis: arXiv

- PIQA: arXiv

- LAMBADA: arXiv

- Sequence Learning: arXiv

- Attention Mechanism: arXiv

- BERT: arXiv

- RMSNorm: arXiv

- Ethical Risks: arXiv

These links provide access to additional resources and further reading related to the concepts discussed in the paper.

## Next

Models

Phi-3-mini

Models

RoBERTa: A Robustly Optimized BERT Pretraining Approach



Gemini 1.5: Unlocking multimodal understanding across millions of tokens of context

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/grouped-query-attention

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

Grouped Query Attention

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# Grouped Query Attention

### Title and Authors

The paper is titled "GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints" and it is authored by Joshua Ainslie, James Lee-Thorp, Michiel de Jong, Yury Zemlyanskiy, Federico Lebrón, and Sumit Sanghai from Google Research.

### Abstract Summary

The abstract introduces a novel method of uptraining existing language model checkpoints into models that utilize grouped-query attention (GQA), a modified version of multi-query attention (MQA). This method enables the models to maintain high quality while achieving faster decoder inference speeds using only 5% of the original pre-training compute.

### Key Concepts

- Multi-query attention (MQA)

- Grouped-query attention (GQA)

- Uptraining from multi-head to multi-query models

- Efficiency in decoder inference

- Trade-off between speed and model quality

- Implementation in transformer models, specifically T5.

### Problem Statement

The paper addresses the challenge of optimizing transformer models for faster inference without significant loss in output quality. Specifically, it focuses on reducing the memory bandwidth overhead from loading decoder weights and attention keys/values, which is a bottleneck in autoregressive decoder inference.

Methods and Techniques:

- Multi-query Attention (MQA): Uses multiple query heads but a single key and value head, reducing memory bandwidth needs.

- Grouped-query Attention (GQA): An intermediate between multi-head attention (MHA) and MQA, using a subgroup of query heads sharing a single key and value head. This approach balances speed and quality better than MQA.

- Uptraining: A method to convert MHA models to MQA or GQA by first averaging the key and value projection matrices from all heads into a single matrix, followed by additional pre-training to adapt the model to its new structure.

### Key Results

The uptrained models using MQA and GQA achieve close to the original quality of multi-head attention models but with significantly improved speed. For instance, the uptrained T5-XXL models show a reduction in inference time from 1.51 ms per sample to 0.28 ms when using GQA, with minimal loss in quality metrics like Rouge and BLEU scores across various datasets.

### Contributions and Innovations

The main contributions include:

- The introduction of grouped-query attention, which provides a novel way to reduce computational load and memory usage without extensive quality degradation.

- Demonstrating a practical uptraining method that allows for the efficient transition from multi-head to multi-query models using existing checkpoints.These innovations could be highly beneficial for ML engineers looking to optimize large language models for faster inference with constrained computational resources.

### Future Work

The authors suggest further exploration in several areas including:

- The application of GQA to encoder layers of the model.

- Detailed analysis and potential improvement of the trade-offs between model speed and quality.

- Evaluation of GQA on decoder-only models, which have been gaining popularity.

### Applications

Possible use cases highlighted include:

- Faster and more efficient machine translation.

- Efficient large-scale document summarization.

- Enhanced performance in question-answering systems where inference speed is critical.

## Relevant Links

- JAX: composable transformations of Python+NumPy programs - JAX

- Flax: A neural network library and ecosystem for JAX - Flax

- Memory-efficient attention implementation - Memory Efficient Attention

- Profile your model with Cloud TPU tools - Cloud TPU Tools

These links pertain to the tools and libraries used in the research, providing resources for implementations and further details on the technologies discussed in the paper.

## Next

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/ai-resources/rotary-position-embedding-rope

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

More Resources

Rotary Position Embedding (RoPE)

Popular

+ View more

Categories

Context WindowsÂ (#)

ModelsÂ (#)

ML SystemsÂ (#)

Updated: August 16, 2024

Read time: #Â mins

# Rotary Position Embedding (RoPE)

Title: RoFormer: Enhanced Transformer with Rotary Position EmbeddingThe authors are Jianlin Su, Yu Lu, Shengfeng Pan, Ahmed Murtadha, Bo Wen, and Yunfeng Liu, all affiliated with Zhuiyi Technology Co., Ltd.

## Abstract Summary:

The paper investigates methods to integrate positional information into the learning process of transformer-based language models and introduces a novel method called Rotary Position Embedding (RoPE). RoPE encodes absolute position with a rotation matrix and incorporates explicit relative position dependencies in self-attention, offering flexibility in sequence length and improving dependency modeling with distance.

## Key Concepts:

- Position encoding in transformers

- Rotary Position Embedding (RoPE)

- Self-attention mechanisms

- Linear and relative position encoding

- Long text classification benchmarks

## Problem Statement:

The main problem addressed is enhancing transformer architectures to effectively incorporate both absolute and relative positional information, improving their performance across various NLP tasks.

## Methods and Techniques:

The study proposes Rotary Position Embedding (RoPE), which uses a rotation matrix to encode absolute positions and explicitly integrates relative positions into the self-attention mechanism. This approach differs from traditional methods by providing flexibility in sequence length and a decaying inter-token dependency with increasing relative distances.

## Key Results:

RoFormer, the transformer enhanced with RoPE, showed improved performance over baseline models on long text classification benchmarks. It demonstrated better handling of sequence lengths and dependency modeling, supported by both theoretical analysis and empirical results.

## Contributions and Innovations:

The primary contributions are the introduction of RoPE for positional encoding in transformers, which enables better dependency modeling between tokens at different positions. RoPE's integration into transformers (RoFormer) shows enhanced performance on NLP tasks, outperforming traditional position encoding methods.

## Future Work:

The authors suggest further exploration of RoPE's application in other transformer-based models and additional NLP tasks. They also indicate the potential for deeper theoretical analysis to understand the observed improvements in model performance.

## Application:

To use Rotary Position Embedding (RoPE) for a language model, a developer can integrate RoPE into the self-attention mechanism of a transformer model. Here’s an example of how this might be implemented in a simplified form using Mojo.



from memory import stack_allocationfrom tensor import Tensor, TensorShapeimport math# Define Rotary Position Embedding in Mojostruct RoPE:    var dim: Int    var inv_freq: TensorF32  # Inverse frequency for position encoding    fn __init__(inout self, dimension: Int) -> Self:        self.dim = dimension        var inv_freq_data = List[Float32]()        for i in range(0, dimension, 2):            inv_freq_data.append(10000.0 ** (-i / dimension))        self.inv_freq = TensorF32(TensorShape(len(inv_freq_data)), inv_freq_data)        return Self {dim: dimension, inv_freq: self.inv_freq}    # Apply RoPE to a TensorSlice (similar to one provided in the example)    fn apply_rope(self, tensor: TensorSlice) -> TensorSlice:        # Calculating sinusoid inputs        var seq_len = tensor.shape().dim(0)        var sinusoid_inp = TensorF32(TensorShape(seq_len, self.dim // 2))        for i in range(seq_len):            for j in range(self.dim // 2):                sinusoid_inp[i, j] = i * self.inv_freq[j]        # Compute sin and cos        var sin = sinusoid_inp.sin()        var cos = sinusoid_inp.cos()        # Apply rotation        var new_tensor_data = BufferPtrFloat32(tensor.data().size())        for i in range(seq_len):            for j in range(0, self.dim, 2):                new_tensor_data[i * self.dim + j] = tensor.data()[i * self.dim + j] * cos[i, j // 2] - tensor.data()[i * self.dim + j + 1] * sin[i, j // 2]                new_tensor_data[i * self.dim + j + 1] = tensor.data()[i * self.dim + j] * sin[i, j // 2] + tensor.data()[i * self.dim + j + 1] * cos[i, j // 2]        return TensorSlice(new_tensor_data, tensor.shape())# Usage Examplevar dim = 64var tensor_shape = TensorShape(100, dim)  # Example shape: 100 sequence length, 64 featuresvar random_data = BufferPtrFloat32.alloc(tensor_shape.num_elements())var example_tensor = TensorSlice(random_data, tensor_shape)var rope = RoPE(dim)var transformed_tensor = rope.apply_rope(example_tensor)

### Key Mojo Adaptations:

- Tensor Management: Direct tensor manipulation using TensorF32 which is a float tensor in Mojo with explicit shape handling.

- Sinusoidal Computation: The sinusoid is computed explicitly with loop indices corresponding to the sequence length and embedding dimension.

- Rotation Application: The rotation based on sine and cosine values is manually applied to the tensor elements.

TensorF32



The RoPE-enhanced self-attention mechanism can be integrated into any standard transformer architecture. This involves replacing the traditional positional encodings in models like BERT, GPT, or any other transformer variant with this new self-attention mechanism.

### Considerations

- Parameter Tuning: Depending on the specific application, the developer might need to tune parameters like the dimension of embeddings or the heads in the attention mechanism.

- Model Training: With RoPE, the model might learn positional dependencies differently. It's essential to monitor the training process to adjust learning rates or other hyperparameters.

This example demonstrates how to implement RoPE within a transformer's self-attention mechanism, potentially leading to better handling of position encoding for long sequences or specific tasks where relative positioning is crucial.

## Relevant Links

- Hugging Face Documentation for RoFormerLink: Hugging Face RoFormerContext: Documentation and model details on the Hugging Face website for RoFormer.

- Link: Hugging Face RoFormer

- Context: Documentation and model details on the Hugging Face website for RoFormer.

- GitHub Repository for RoFormerLink: ZhuiyiTechnology RoFormerContext: GitHub repository containing the source code and implementation details for RoFormer.

- Link: ZhuiyiTechnology RoFormer

- Context: GitHub repository containing the source code and implementation details for RoFormer.

- NeurIPS Paper on Transformer ArchitecturesLink: NeurIPS 2017 PaperContext: The foundational paper "Attention is All You Need" introducing transformer architectures, influential in the development of models like RoFormer.

- Link: NeurIPS 2017 Paper

- Context: The foundational paper "Attention is All You Need" introducing transformer architectures, influential in the development of models like RoFormer.

- OpenReview - ELECTRALink: ELECTRA on OpenReviewContext: Discusses the ELECTRA model, which is related to the discussion on efficient transformer models like RoFormer.

- Link: ELECTRA on OpenReview

- Context: Discusses the ELECTRA model, which is related to the discussion on efficient transformer models like RoFormer.

- ALBERT Model on OpenReviewLink: ALBERT on OpenReviewContext: Details about the ALBERT model which optimizes the training of transformers by reducing parameters.

- Link: ALBERT on OpenReview

- Context: Details about the ALBERT model which optimizes the training of transformers by reducing parameters.

- ELECTRA PDF on OpenReviewLink: ELECTRA PDFContext: The PDF document of the ELECTRA paper discussing discriminators rather than generators for training language models.

- Link: ELECTRA PDF

- Context: The PDF document of the ELECTRA paper discussing discriminators rather than generators for training language models.

- Findings of EMNLP 2020Link: ACL Anthology EMNLP 2020Context: The findings from EMNLP 2020, which includes research and developments related to transformer models and NLP.

- Link: ACL Anthology EMNLP 2020

- Context: The findings from EMNLP 2020, which includes research and developments related to transformer models and NLP.

- ICML 2020 on Continuous Dynamical ModelsLink: ICML 2020 Liu et al.Context: Discusses encoding position in transformers using continuous dynamical models.

- Link: ICML 2020 Liu et al.

- Context: Discusses encoding position in transformers using continuous dynamical models.

- NeurIPS 2018 on Neural Ordinary Differential EquationsLink: Neural ODEsContext: A groundbreaking approach using ordinary differential equations for modeling continuous dynamics in deep learning, relevant to advancements in understanding transformer architectures.

- Link: Neural ODEs

- Context: A groundbreaking approach using ordinary differential equations for modeling continuous dynamics in deep learning, relevant to advancements in understanding transformer architectures.

- Lucidrains' Performer-PyTorch GitHub RepositoryLink: Performer PyTorchContext: A repository for the Performer model, an efficient transformer variant that scales linearly with sequence length, implemented in PyTorch.

- Link: Performer PyTorch

- Context: A repository for the Performer model, an efficient transformer variant that scales linearly with sequence length, implemented in PyTorch.

- Link: Hugging Face RoFormer

- Context: Documentation and model details on the Hugging Face website for RoFormer.

- Link: ZhuiyiTechnology RoFormer

- Context: GitHub repository containing the source code and implementation details for RoFormer.

- Link: NeurIPS 2017 Paper

- Context: The foundational paper "Attention is All You Need" introducing transformer architectures, influential in the development of models like RoFormer.

- Link: ELECTRA on OpenReview

- Context: Discusses the ELECTRA model, which is related to the discussion on efficient transformer models like RoFormer.

- Link: ALBERT on OpenReview

- Context: Details about the ALBERT model which optimizes the training of transformers by reducing parameters.

- Link: ELECTRA PDF

- Context: The PDF document of the ELECTRA paper discussing discriminators rather than generators for training language models.

- Link: ACL Anthology EMNLP 2020

- Context: The findings from EMNLP 2020, which includes research and developments related to transformer models and NLP.

- Link: ICML 2020 Liu et al.

- Context: Discusses encoding position in transformers using continuous dynamical models.

- Link: Neural ODEs

- Context: A groundbreaking approach using ordinary differential equations for modeling continuous dynamics in deep learning, relevant to advancements in understanding transformer architectures.

- Link: Performer PyTorch

- Context: A repository for the Performer model, an efficient transformer variant that scales linearly with sequence length, implemented in PyTorch.

These links provide access to resources, repositories, and papers that expand on the theoretical and practical applications of advanced transformer models like RoFormer and their relative position encoding methods.

## Next

Models

Byte Pair Encoding (BPE)



YaRN: Efficient Context Window Extension of Large Language Models



Attention with Linear Biases Enables Input Length Extrapolation (ALiBi)



LLM Context Evaluations

On this page

Download for your platformÂ now

- 

- 

- 

View License

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Context Windows

ML Systems

ML Systems

Context Windows

ML Systems

Context Windows

ML Systems

Context Windows

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Models

Models

Models

ML Systems

ML Systems

Context Windows

================================================================================

URL: https://www.modular.com/legal/champions

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# Modular Community Champions Policy

Last Updated: June 2nd, 2023This Community Champions Policy (this âPolicyâ) forms part of Modularâs Terms of Use and governs applicable User conduct for Users who are appointed âChampionsâ by Modular in Modularâs third party-online forums for Mojo, such as its Discord, Github Discussions or similar forums (each a âForumâ). Modular may designate Champions and revoke âChampionâ status of any User in its sole discretion, for any reason or no reason. Modular reserves the right to suspend and/or terminate Champion-User accounts for any violations of this Policy, violations of any terms governing use of the applicable Forum, or any other violations of Modularâs Terms of Use.Â

##### Facilitating a Stable and Respectful Community

Champions are expected to uphold community rules, norms, and expectations in the applicable Forum that encourage positive engagement by community members. Champions shall both abide by the applicable Modular terms and Forum terms, and encourage community members of the applicable Forum to do the same. The content in each Forum that is subject to this Policy includes, but is not limited to:

- Posts

- Comments

- Discussions

##### Setting Community Member Expectations

Champions can ensure Forum members have predictable experiences on various rooms, communities, threads or other areas of a Forum by doing the following:

- Providing a clear and concise description of the topic(s) discussed by your applicable community.

- Properly labeling content and communities, particularly content that is graphic, sexually-explicit, or offensive.

- Creating rules that explicitly outline your expectations for members of your community.

- Explicitly marking your community as âunofficialâ in the community description

##### Preventing Undesirable Conduct

As a Champion, Users shall not interfere with or disrupt other, non-Mojo communities, nor shall they facilitate, encourage, coordinate, or enable members of the applicable Forum community to do the same.Champions shall never do the following, and shall monitor and moderate content in the applicable Forum community that attempts to do the following:

- Mention other communities, and/or content or users in those communities, with the effect of inciting targeted harassment or abuse.

- Enable or encourage members of a Forum to violate applicable terms.

- Enable or encourage community members to post or repost content in other communities that is forbidden.

##### Engagement

Champions shall regularly check in and moderate content on and otherwise engage with content on the applicable Forum in the designated communities. Champions shall ensure that issues are being addressed, and that Forum community members feel safe and attended to.

##### Enforcing Forum Guidelines

Modular will strive to work with Champions directly to resolve issues happening in the applicable Forum without having to resort to restrictive measures. If Champions are unable to moderate community members appropriately because an applicable member is unresponsive or responds with hostility or is uncooperative, Modular may work directly with the applicable Forum providers to take corrective actions.

###### Miscellaneous

Champions agree and acknowledge that any time spent acting as a Champion is unpaid and on a voluntary basis, and further understand that serving as a Champion in no way creates an employment relationship or other services relationship with Modular. Modular shall have no liability or responsibility to Champion for its conduct in any Forum or as a result of this Policy. Â Other than as explicitly authorized in this Policy, Champions may not speak on behalf of Modular. Except for authorized Forum activity, Champions shall not make public statements about Modular or Mojo without disclaiming Champions lack of affiliation with Modular.

###### Privacy

Misuse of Personal Information is prohibited. Any person or entity collecting data from the Platform must comply with the Modular Privacy Policy (https://www.modular.com/legal/privacy). If a User collects any Personal Information from the Platform, that User agrees it will only use that Personal Information for the purpose for which the respective User to whom the Personal Information pertains has authorized its use. User agrees that it will reasonably secure any Personal Information it has gathered from the Platform, and User will respond promptly to complaints, removal requests, and "do not contact" requests from us or other Users (to the extent applicable).

###### User Protection

User must not engage in activity that significantly harms other Users. Modular will interpret policies and resolve disputes in favor of protecting Users as a whole.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/contact/us

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog/how-mojo-gets-a-35-000x-speedup-over-python-part-1

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

August 18, 2023

# How Mojoð¥ gets a 35,000x speedup over Python â Part 1

Abdul Dakkak

When we announced Mojo, we claimed that Mojo can be 35,000 times faster than Python and we demonstrated this using a specific compute-bound problem: generating a Mandelbrot set. This is an impressive number that has garnered some scrutiny (as it should). In this blog post series, we will show you how we arrived at this figure and the optimizations we implemented to achieve the performance.

In this first part of the series, we'll start by motivating the Mandelbrot problem and why itâs a good candidate to measure performance. We will then describe some basic optimizations you can do in Mojo to get faster performance over Python code. Finally, we will compare our optimized implementation in Mojo against both pure-Python code and Python code using the NumPy library, and discuss why high-performance libraries like NumPy and other programming frameworks are not best suited to handle this problem.Â

While these basic optimization techniques show that Mojo is much faster than Python, they alone wonât net you 35,000x speedup. As we continue through this blog series, we will introduce other Mojo features that you'll need to achieve the 35,000x speedup.

### What is the Mandelbrot set?

The Mandelbrot set is the set of complex numbers $c$ for which the function $z_{n+1}={z_n}^2+c$Â  does not diverge when evaluated iteratively from $z=0$ at each point $c$ in the complex plane. In simpler terms, the algorithm repeatedly squares $z_n$ and adds it to $c$ to get the next iteration valueÂ  $z_{n+1}$. For some points this sequence will diverge and for some it will converge - this will determine whether the point belongs to the set or not.

There are multiple ways to compute the Mandelbrot set. The simplest is the âescape time algorithmâ which looks like the following in both Python and Mojo:

MAX_ITERS = 1000
def mandelbrot_kernel(c): 
  z = c
  nv = 0
  for i in range(MAX_ITERS):
    if abs(z) > 2:
      break
    z = z*z + c
    nv += 1
  return nv

â

The algorithm iterates up to the âescapeâ condition (when the norm is greater than 2) or it âbailouts outâ after MAX_ITERS iterations (set to 1000 throughout this blog post). The âescape time algorithmâ is commonly used as a benchmark for demonstrating performance. If we plot which points are in the set and which ones are not, we get the following pictorial representation.

â

There are other algorithms that take advantage of the periodicity, symmetry, and other features of the Mandelbrot set. For our benchmarking purposes, we are interested in the brute force approach to see how much we can push Mojo to best utilize the hardware.

### Why Mandelbrot?

If youâre wondering why we should even care about the Mandelbrot set â the simple answer is that we do not. But from a compute perspective, the Mandelbrot set algorithm has a few interesting properties:

- Simple to express: Mandelbrot is simple to understand and write. It acts as a proxy to many compute heavy iterative operations.

- Pure compute: Mandelbrot has no memory overhead (i.e. you perform quite a bit of computation per output pixel). Furthermore, the amount of work to be done per output pixel is adjustable via the max iteration factor or the domain of the points.â

- Irregular computation: adjacent pixels do not necessarily belong in or out of the set, and efficient computation requires early escape. Hence, the computation cannot be efficiently expressed in systems that only operate on regularly structured loop nests (e.g. array-based programming or polyhedral programming paradigms).â

- Embarrassingly parallel: The Mandelbrot computation is easy to parallelize. This means that each pixel computation is independent of the adjacent one.Â â

- Vectorizable: The Mandelbrot computation can be written to utilize the vector operations available on modern CPUs.

### How to speed up the Mandelbrot algorithm

At a high-level, the recipe to achieve maximal performance is to utilize the available hardware to its theoretical limits, so you are only bound by the âphysicsâ of the hardware â but reaching those limits is easier said than done ð.

The simpler answer to speed up your code is: just use Mojo. The code below is Mojo code. We welcome folks to try the Mojo Mandelbrot notebook that is in the Mojo Playground to experiment with this code.

def mandelbrot_0(c: ComplexFloat64) -> Int:
    z = c
    nv = 0
    for i in range(1, MAX_ITERS):
      if abs(z) > 2:
        break
      z = z*z + c
      nv += 1
    return nv

â

In fact, we just took the same Python code we shared earlier and added type annotations (see the introductory blog post on Mojo for Python users and the Mojo programming manual). Mojo uses these types to optimize the generated code. Note, that here we only annotated the function input types and everything else remains the same.

A further optimization we can do is to opt into the Mojo âstrictâ mode. This will enable Mojo to perform more aggressive optimizations since the compiler can discern certain properties about the program. You opt into the strict mode by using fn instead of def and declaring the variables:

fn mandelbrot_1(c: ComplexFloat64) -> Int:
    var z = c
    var nv = 0
    for i in range(1, MAX_ITERS):
      if abs(z) > 2:
        break
      z = z*z + c
      nv += 1
    return nv

### Simplifying the math to reduce computation

Once we switch to Mojo, we can look at what further optimizations can be done. The first thing to try is to make the sequential implementation better by removing redundant computations. We can do two things here. One is to avoid the square root, by changing the check of abs(z) > 2 to be squared_norm(z) > 4. A square root typically takes around 6 flops (floating point operation).

The other is by simplifying the computation of $z^2+c$ which would be naively computed as $x*y +c$ and expanded to:

$$ (x_{re} y_{re}-x_{im} y_{im}+c_{re} , x_{re} y_{im}+x_{im} y_{re}+c_{im}) $$

Since $x=y$ in our example, we can simplify the equation to:

$$ (x_{re} x_{re}-x_{im} x_{im}+c_{re} , 2x_{im}x_{re} + c_{im}) $$

This saves a further single flop.

In total, we save around 7 FLOPS. This might not sound like much, but this operation is in the innermost loop and these FLOPS add up.

Squared addition is a common operation, and the Mojo standard library provides a special function for it called squared_add, which is implemented using FMA instructions for maximum performance.

struct Complex[type: DType]:
    ...
    fn squared_add(self, c: Self) -> Self:
        return Self(
            fma(self.re, self.re, fma(-self.im, self.im, c.re)),
            fma(self.re, 2*self.im, c.im)))

â

We can rewrite our code to utilize this function as follows:

fn mandelbrot_2(c: ComplexFloat64) -> Int:
    var z = c
    var nv = 0
    for i in range(1, MAX_ITERS):
        if z.squared_norm() > 4:
            break
        z = z.squared_add(c)
        nv += 1
    return nv

### Putting it all together

Up until now, we have implemented 3 variants of the Mandelbrot kernel: mandelbrot_0, mandelbrot_1 and mandelbrot_2. This is the core compute operation, but we still need to iterate through the pixels in the image and compute the membership in the set. Again, this is straightforward in Mojo:

alias height = 4096
alias width = 4096
alias min_x = -2.0
alias max_x = 0.47
alias min_y = -1.12
alias max_y = 1.12
alias scalex = (max_x - min_x) / width
alias scaley = (max_y - min_y) / height

for h in range(height):
	let cy = min_y + h * scale_y
	for w in range(width):
		let cx = min_x + w * scale_x
		output[h,w] = mandelbrot_N(ComplexFloat64(cx,cy))

â

With the above, you can replace mandelbrot_N with mandelbrot_0, mandelbrot_1 and mandelbrot_2 to evaluate the Mojo implementation against Python over the same domain, iteration count, and datatype (Python uses double-precision and so do we).

We use an Intel Xeon Platinum 8481C CPU for all evaluations, and the results are shown below:

â

There are a few things to note here. First, by just switching to Mojo we achieved a 46x speedup. Second, there is no difference in performance between mandelbrot_0 andÂ  mandelbrot_1. The reason is that Mojoâs type inference and optimizations remove the dynamism from the types â allowing one to work with concrete types rather than variants.

And by introducing squared_norm and squared_add we achieved a 89x speedup over Python

### What about NumPy?

NumPy is a popular library in Python that brings an array programming language paradigm to Python. The idea of NumPy is that we can provide C implementations of common Python operations and by operating large arrays (instead of scalars) we can amortize the Python interpreter overhead.

There are multiple ways to implement the Mandelbrot algorithm using NumPy. One option is to use numpy.vectorize, and the other way is operate on the entire region at once relying on Numpy itself to perform that operation efficiently. The code snippet below shows the second approach:

zs = np.zeros((n, m), dtype=complex)
mask = np.full((n, m), True, dtype=bool)
for i in range(MAX_ITER):
    zs[mask] = zs[mask] * zs[mask] + cs[mask]
    mask[np.abs(Z) > 2] = False

â

Pictorially, whatâs happening above is that we are updating all the values in the Mandelbrot set in lockstep based on the mask each time and selectively updating the mask values.

â

So instead of our vectorization approach where it is unlikely that we need 1000 iterations of work (which could happen at the boundary of the fractal), the NumPy implementation above is guaranteed to waste work. As you see above, it performs MAX_ITERS operations on every pixel in the image unconditionally. With NumPy you only has limited control on the algorithm and how it works and is implemented, and thus it cannot perform the optimizations we mentioned earlier.

As a result, a NumPy implementation is only 5x faster than Python:

### Conclusion

In this blog post, we motivated the Mandelbrot example. We also presented more detailed performance numbers comparing Mojoâs implementation of Mandelbrot vs existing solutions. We also described why existing solutions are not well-equipped to solve this problem.

Clearly this is not the 35,000x speedup that we showcased in the launch. In the next blog post we will shed some light on the next set of Mojo features that enable the 35,000x speedup. We will also share some insights as to why Mojo is ideally positioned to solve this and other performance programming problems.

Mandelbrot is a nice problem that demonstrates the capabilities of Mojo. We use similar methodologies to author all of our Library and Kernel functions that power our AI engine to enable speed and efficiency across our stack â without sacrifice to generality and good programming practices. If you are interested in making things fast or in general interested in building and driving state-of-the-art AI infrastructure from the ground up, then consider joining Modularâs exceptional team by checking out our careers page. And consider joining our awesome community on Discord and share your Mojo journey with us on social media.Â

Until next time ð¥!

â

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Developer

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Abdul Dakkak

AI Compiler Engineer

Expert in machine learning, compilers, programming languages, and accelerated computing. Before Modular, Abdul led the development of AI compilers for GPUs at Microsoft Research and the Mathematica Compiler at Wolfram Research. Abdul has developed open-source tools for accelerating real-world applications to optimize their performance across the hardware and software stack.



================================================================================

URL: https://www.modular.com/blog/how-mojo-gets-a-35-000x-speedup-over-python-part-2

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

August 28, 2023

# How Mojoð¥ gets a 35,000x speedup over Python â Part 2

Abdul Dakkak

In the previous blog post, we motivated the Mandelbrot set problem and described basic optimization techniques to get around 90x speedup over Python. In this blog post (part 2 of our 3 part blog post series), we continue our optimization journey and describe how to go from 90x to 26,000x speedup over Python. We will share insights into the techniques we use and discuss why Mojo is well positioned to address them. In the upcoming and final part 3, we'll wrap this series up by showing you how you can get well over 35,000 speedup over Python.

How do I run this example: Last week we announced that Mojo SDK will become available for local download to everyone in early September. Sign up to be notified if you haven't already! This example will become available in the Mojo examples repository on GitHub along with Mojo SDK availability. And don't forget to join us live on our first ever Modular Community Livestream where we'll share recent developments in Mojo programming language and answer your questions about Mojo.

First, letâs recap what we've discussed so far. In the previous blog post, we showed the how to express the Mandelbrot set in Mojo:

fn mandelbrot_blog_post_2(c: ComplexFloat64) -> Int:
    var z = c
    var nv = 0
    for i in range(1, MAX_ITERS):
        if z.squared_norm() > 4:
            break
        z = z.squared_add(c)
        nv += 1
    return nv

â

We then used the following loop to check whether each point is within the set:

alias height = 4096
alias width = 4096
alias min_x = -2.0
alias max_x = 0.47
alias min_y = -1.12
alias max_y = 1.12
alias scale_x = (max_x - min_x) / width
alias scale_y = (max_y - min_y) / height

for h in range(height):
	let cy = min_y + h * scale_y
	for w in range(width):
		let cx = min_x + w * scale_x
		output[h,w] = mandelbrot_blog_post_2(ComplexFloat64(cx,cy))

â

The above code produced a 90x speedup over Python and a 15x speedup over NumPy as shown in the figure below:

â

In this blog post, we are going to continue on this journey and describe other optimizations that can get us closer to the 35,000x speedup we reported during launch. We will still use the 88-Core Intel Xeon Platinum 8481C CPU as we did in the first blog post in this series.Â

### Vectorizing the code

Continuing with the sequential optimizations from the first blog post, the next thing we can do is to switch from using scalars to using vector operations. Vectorization, also known as Single Instruction Multiple Data (SIMD) has been around since the 1960s and allows one to operate on multiple data elements via a single instruction. Vector operations were added to commodity PCs in the mid 1990s (e.g. in 1997 in the Pentium P5 processor). But, despite being pervasive in hardware, many programming languages still donât have first class support for it. For example, C++ doesnât have standard support for SIMD (though there is an experimental proposal and many third-party libraries), and SIMD support in more modern languages is still experimental or subject to caveats.

In Mojo, the SIMD type is a first-class type. To take advantage of SIMD to operate on multiple pixels at a time, we have to write our code slightly differently from the scalar code shared earlier. This is especially true because each pixel may escape at a different iteration count. The resulting code is still straightforward, though:Â

fn mandelbrot_kernel(c: ComplexSIMD[DType.float64, simd_width]) -> \
                        SIMD[DType.index, simd_width]:
    var z = c
    var iters = SIMD[DType.index, simd_width](0)
    var in_set_mask = SIMD[DType.bool, simd_width](True)

    for i in range(MAX_ITERS):
        if not in_set_mask.reduce_or():                 # (1)
          break                   
        in_set_mask = z.squared_norm() <= 4             # (2)
        iters = in_set_mask.select(iters + 1, iters)    # (3)
        z = z.squared_add(c)                            # (4) 
    return nv

â

Code snippet above uses a vector mask to keep track of which pixels are still in the set and which pixels have escaped. Letâs dissect what each line in the loop means:

- If none of the elements in the vector lane are in the set, then we exit the loop.

- Compute the mask. i.e. if the SIMD width is 4, then a mask of [True, True, False, False] means that the first 2 elements in the 4 element patch did not diverge while the latter 2 did.

- We increment the values of the pixels that did not escape by 1.

- We update the z value unconditionally.

We can compute the above for each pixel in the image by using our vectorize method:Â

for h in range(height):
  let cy = min_y + h * scale_y
  
  @parameter
  # (1)
  fn compute_vector[simd_width:Int](w:Int): 
    # (2)
    let cx = min_x + (w+iota[DType.float64, 
                      simd_width]()) * scale_x
    # (3)
    output.simd_store[simd_width](Index(h,w),
    	                mandelbrot_kernel(
                                        ComplexSIMD[DType.float64, 
                                        simd_width](cx,cy))
  # (4)
  vectorize[simd_width, compute_vector](width)

â

We again explain each step in this process:

- The compute_vector function is parameterized on a simd_width as a parameter and a runtime argument of the width of the image.Â

- We compute the x positions in the complex plane using the iota method. The iota function gives the sequence [0, 1, 2, â¦, simd_width-1] and that enables us to construct a SIMD vector representation of the x positions in the complex plane.

- We call our Mandelbrot kernel with the ComplexSIMD value. Note that Mojo supports overloading so there is no need to rename the function.

- We use the vectorize high-order generator to apply compute_vector on all elements of width in a vectorized manner, while handling the edge cases.

Pictorially whatâs happening is that we are computing the Mandelbrot in simd_width chunks where each chunk is computed using vector operations. Note that while the image shows multiple rows are computed at the same time, this is purely for pictorial purposes since it would be hard to see.Â

â

Recall that in the first blog post we only achieved 90x speedup over Python. The theoretical expectation from the vectorization is around 8x speedup (the system has a 512-bit vector register, therefore we have 512/64=8 double-precision elements per SIMD vector). We can plot the speedup against Python and the best results from the first blog post (marked as blog1):

â

By vectorizing the implementation we are able to achieve a 527x speedup over Python and a 6x speedup over the first blog post results. Thatâs not an 8x speedup, however. One of the reasons why we do not get the 8x speedup is because our code performs a reduction across the vector lanes. The other reason is that escape condition is not the same across all elements in the vector, so there is extra work thatâs done on the fractal boundary.

### Increasing work per loop iteration

Still, thatâs not good enough and much less than our 35,000x speedup goal. One thing to explore is the ratio of useful work (actually compute) to loop logic. The current ratio of the loop overhead to the amount of compute is not great if we have our SIMD width equal to the architectural SIMD width for tiny compute operations. Mojo allows us to operate on wider vectors and thus amortizing the overhead. Note that using the longer vector width is akin to, but not precisely the same as, loop unrolling.Â

Modern x86 systems have multiple fused multiply-add (FMA) units which enables them to execute multiple FMAs every clock cycle. On the system we are using for benchmarking there are 2 FMA ports, so we can set the SIMD width to be a multiple of the hardware SIMD width. By using twice the hardware SIMD width, you increase the amount of work per loop iteration and create two FMA paths to increase utilization of the FMA pipelines and thus increasing instruction level parallelism (ILP). Of course, this is machine dependent, and we could have alternatively used Mojoâs autotuning feature here to find the right constant.

alias num_ports = 4 # Is autotuned

fn mandelbrot_kernel(c: ComplexSIMD[DType.float64, 
                                    num_ports * simd_width]) -> \
                                    SIMD[DType.index, 
                                         num_ports * simd_width]:
    var z = c
    var iters = SIMD[DType.index, num_ports * simd_width](0)
    var in_set_mask = SIMD[DType.bool, num_ports * simd_width](True)

    for i in range(MAX_ITERS):
        if not in_set_mask.reduce_or():
        	break                   
        in_set_mask = z.squared_norm() <= 4    
        iters = in_set_mask.select(iters + 1, iters)    
        z = z.squared_add(c)                   
    return iters

â

In terms of performance, we can evaluate the speedup as we vary the num_ports and select the best one (i.e. autotune). If we plot the results, we can see that the value num_ports=4 is the ideal sweet spot (with a 1.6x speedup) and that as we increase the number of ports we degrade the performance since we increase contention.

â

â

### Parallelizing the code

So far we have only looked at a single-threaded implementation (with vectorization), but modern hardware has multiple cores. A natural way to parallelize is to subdivide the complex plane into a sequence of rows, where each thread gets a sequence of rows to operate on.

In Mojo, this can be implemented using the parallelize method:

fn compute_row(chunk_idx:Int):
  let y = chunk_size * chunk_idx
  let cy = min_y + y * scale_y

  @parameter
  fn compute_vector[simd_width:Int](w:Int):
  	let cx = min_x + iota[DType.float64, simd_width]() * scale_x
  	output.simd_store[simd_width](Index(h,w), 
                                  mandelbrot_kernel(
                                               ComplexSIMD[DType.float64, 
                                               simd_width](cx,cy))
  vectorize[num_ports * simd_width, compute_vector](width)

  with Runtime(num_cores()) as rt:
  	parallelize[compute_row](rt, height)

â

Pictorially, whatâs happening is that we compute chunks of rows in parallel, with each thread assigned to a sequence of rows.

â

â

In terms of speedup, we are able to get 26,194x speedup over the Python implementation by using all the 88 cores available on the system:

â

However, there is a problem with the above approach. With 88 cores we should expect around an 88x speedup, but we were only getting a 30x speedup. In the next blog post we will describe what this problem is and how we can address it to achieve well over 35,000x speedup.

### Conclusion

Let's summarize our optimization journey so far in these 2 blog posts:

- In part 1 we started by just taking our Python code and running it in Mojo, we then type annotated the Mojo code, and performed some algebraic simplifications see a 90x speedup up over Python.

- In this blog post, we started with the code from the first part and vectorized it. We then updated the code to utilize multiple FMA ports, and finally we changed the sequential implementation into a parallel one to take advantage of all the cores on the system to get over 26,000x speedup over Python.

â

Below is the same chart in log scale, where it's easier to see the exponential speedup going from Python -> Mojo:

â

As you can see above, our implementation achieves over 26,000x speedup over Python. In the next blog post, we are going to discuss the next set of optimizations to get over the 35,000x speedup. Stay tuned!

Throughout these two blog posts, we showed how using Mojo programming language with the knowledge of the problem and hardware insights, enables us to get massive performance improvements. We employ the same methodology in our internal Modular Kernel development cycle. If you are interested in making algorithms fast or in general interested in building and driving state-of-the-art AI infrastructure from the ground up, then consider joining Modularâs exceptional team by checking out our Kernel engineer role or other roles available on our careers page.

Don't forget to join us live on our first ever Modular Community Livestream where we'll share recent developments in Mojo programming language and answer your questions about Mojo. We will also be releasing Mojo SDK generally to everyone in early September. Sign up to download.

Until next time ð¥!

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Developer

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Abdul Dakkak

AI Compiler Engineer

Expert in machine learning, compilers, programming languages, and accelerated computing. Before Modular, Abdul led the development of AI compilers for GPUs at Microsoft Research and the Mathematica Compiler at Wolfram Research. Abdul has developed open-source tools for accelerating real-world applications to optimize their performance across the hardware and software stack.



================================================================================

URL: https://www.modular.com/team

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/career-post?4234627005&gh_jid=4234627005

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

CAREERS

/



# 



- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/careers

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# Join us in building the future of AI

Watch Video

## We want to enable AI to be used by anyone, anywhere.

Our ambitions are enormous, but working here will feel very familiar. You can change the world without giving up your life

## Why Modular?

Grow with the best

Build with some of the industry's best AI leaders.

Maximize how you work

We will always push the limits to create the best possible environment for our people and teams.

Read how we work.

Build AI for the world

Build a next-generation developer platform, with production quality infrastructure, for the world.

Have fun, live life

Regular team onsites, local meetups & fun, strong team collaboration and more.

## Work and life, balanced.

ð©º

Leading medical, dental & vision packages

ðµ

Strong compensation & equity packages

ð¼

Generous maternity & paternity leave

ð

401K Plan

ð

Work wherever you want

ðï¸

Unlimited Vacation & PTO

ð¥³

Corporate perks & epic team fun

## Current Job Openings

Come and be part of a world-class team that is rebuilding AI for everyone. We welcome applications from all backgrounds and communities.

Engineering

(0)

Text

Location

## What you expect from the interview

Modular has a straight-forward interview process and a large majority of the roles follow the same steps:

Step 01

### Application Review

The first step of the process is to submit your resume. We aim to review all resumes within 2 weeks and we respond to every single application made.

Step 02

### Call with Recruiting

Typically you will first have a short 20-30min call with recruiting to just go over some basic information about you and the company, and to discuss next steps.

Step 03

### Call with Hiring Manager

The Hiring Manager will dig a bit deeper into your background and previous experience to determine mutual fit. This will also be your opportunity to learn more about what the team is working on and what the role entails. In case you have applied for more than one role on different teams, we often schedule you for two separate calls with two separate Hiring Managers.

Step 04

### Team Interviews

There are typically 4 team interviews, usually 45-60 minutes each. These may be scheduled all at once or separately, depending on the role. The focus areas for these team interviews vary by role. For example, for Engineering roles, you can expect coding and system design type interviews; for Product roles, we often talk about the industry and product strategy. For Leadership roles, we may ask for a presentation.We believe that team culture is a critical part of a great company, so every candidate also has a Culture interview that focuses on communication skills, team work, and so on. Â We will let you know what to expect in each interview when we send you a schedule confirmation.

Step 05

### Founder Interviews

We aim to put all finalists in touch with at least one of our co-founders before a final decision. This would allow you as a candidate to speak directly to Chris and/or Tim, and get answers to any final important questions you may have.

Step 06

### Offer

If we make you an offer, we share an extensive offer deck for you and schedule a walk-through call to go over the information in detail and again answer any final questions you may have. We also aim to give you enough time to make a decision after that.

## Why should you be excited aboutÂ Modular?

âI picked modular because of the people and what they believe in and what they want to do. I want to be a part of something that makes a difference for the better.â

Paige Bedwell

Program Manager

âThe team is incredible. They're very smart. They're experts in their field, and we're solving the hardest computational problems in the world.â

Abdul Dakkak

AI Compiler Engineer

âThe best part of working here is a strong commitment to culture. I work on the most challenging problems, have the freedom to do it anywhere, and have a huge impact on AI.â

Eric Johnson

Product Lead

## Frequently asked questions

Are interviews conducted in-person?

Typically of our interviews are conducted over Zoom video. For coding interviews, we use Coderpad and you will receive a link from your interviewer. In some cases we may fly you in to meet some of our leaders for the last interview.

What do you look for in a candidate?

There is no âone size fits allâ answer here however Modulers are passionate about their work, intellectually curious, humble, and ready to roll up their sleeves. We like to have discussions in interviews and expect you to ask clarifying questions and talk us through your thought process â sometimes there is no right or wrong answer, we just want to understand how you think and work through challenging problems. We also look for strong alignment with Modularâs Culture and values.

I donât meet all of the requirements for a role, can I still be considered?

We would love to receive your application even if it doesnât meet all criteria! We review applications with all roles and opportunities in mind. Sometimes we may reach out to you to offer or suggest another role or team that might be a better fit for you, or we offer to keep your application on file and reach out a few months down the line once more suitable opportunities open up.

How long does the interview process take?

On average it takes candidates about 4 weeks to go through the full interview process however if you require a decision by a certain date, please speak to your recruiter.

Do you offer internships?

We want to ensure that our interns have an outstanding experience, which includes a well-defined project and goals, meaningful and impactful work, and strong and attentive mentorship. As we are still a small team, we are not ready to offer internships at this time but we will be sure to update the website as soon as it changes!

Do you provide visa assistance?

Yes, we offer premium processing for our H1-B transfer candidates. We can also sponsor H1-B or offer other relevant visa application assistance to eligible candidates.

Do you provide Green Card assistance?

Yes, we also offer Green Card application assistance to eligible candidates, just speak to your recruiter for more details.

Do you provide relocation assistance?

As we are a remote-first company, we generally do not relocate candidates except new graduates who are moving away from campus. If Modular asks you to relocate, we will also provide you with a relocation stipend.

Are there any opportunities for professional development?

Modular is growing rapidly so there are a variety of growth opportunities available â whether thatâs moving into a TL role, management, or a lateral transfer to a different team. We provide our managers with training to walk you through your career planning, and we have a well-defined but lightweight performance review process that ensures your career goals are discussed at a regular cadence.

I applied some time ago but havenât heard back?

We aim to respond to every single candidate, and if you have applied to more than one role, we process all of your applications separately. Sometimes these emails end up in spam, so please check your spam folder. If you havenât heard from us, you can contact your recruiter or careers@modular.com to follow up.

Will you provide feedback if I am rejected?

We sincerely appreciate the time it takes to interview, so we always provide some feedback as to why your application did not work out if we have spoken to you. If you have not heard from us within 2 weeks after your last call, please follow up with your recruiter.

What benefits do you offer?

Modular offers some world-class benefits for all employees. Our benefit package changes over time, and may depend on your specific location, but US-based employees can expect benefits that include:

- A variety of fantastic health benefits (health, dental, vision insurance; life insurance etc) are available.

- A 401k plan with up to 5% match.

- Access to Passport Corporate Discount Program, LinkedIn Learning, and free tax advice on Carta.

- Generous work-from-home stipend of $1500 to help you improve your home office.

- Unlimited paid time off and flexible work hours.

Who we are

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/team/abdul-dakkak

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?author=Abdul+Dakkak

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?topic=Engineering

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/team/mikhail-zolotukhin

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?author=Mikhail+Zolotukhin

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/?9cedfb3e_page=2

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

80K+ developers building with MAX

# A high-performance generative AI framework

MAX replaces and simplifies your AI inference stack, maximizing performance and reducing cloud costs

## Achieve state of the art NVIDIAÂ GPU performance

Unlock state of the art latency and throughput without writing low-level CUDA code.

### Deploy MAX on GPUs

## Optimize your existing PyTorch & ONNX models

Migrate seamlessly without rewriting your AI models and pipelines on a unified AI stack.

## Use Mojo to supercharge your AIÂ applications

Extend your PythonÂ code with high-performance Mojo, a new programming language with the expressiveness of Python and the performance of C.

## Develop locally, deploy globally to any cloud.

Develop your AI applications locally and package and deploy across any cloud provider, on CPUs and GPUs, without having to change your code.

## Run Gen AI locally underÂ 3Â minutes

Download MAX first to run an LLM

- Run Llama3-8B in your CLIGet started

- Run Llama2-7B in your CLIGet started

- Run Replit-3B in your CLIGet started

Run Llama3-8B in your CLI

Run Llama2-7B in your CLI

Run Replit-3B in your CLI

## Free Forever

The MAXÂ framework is a free and open platform for you to develop and deploy AI inference workloads.

A new framework for Gen AI, and the best way to deploy PyTorch

Development tools for accelerated compute on GPUs and CPUs, built from the ground up for GenAI, but compatible with today.

## Developer Approved ð

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

## Get started now

- Getting started guideStep by step walkthrough on how to get started

- Browse examplesRun a project locally in just 2min

- Connect with buildersJoin over 22,000 developers

Getting started guide

Step by step walkthrough on how to get started

Browse examples

Run a project locally in just 2min

Connect with builders

Join over 22,000 developers

Download for your platformÂ now

- 

- 

- 

View Pricing

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

MAX on GPU waitingÂ list

Be the first to get lightning fast inference speed on your GPUs. Be the envy of all your competitors and lower your compute spend.

================================================================================

URL: https://www.modular.com/team/chris-lattner

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/team/ehsan-m-kermani

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?author=Ehsan+Kermani

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog/mojo-pi-approximating-pi-with-mojo-using-monte-carlo-methods

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

March 14, 2024

# Mojoð¥ â¤ï¸ Pi ð¥§: Approximating Pi with Mojoð¥ using Monte Carlo methods

Shashank Prasanna

March 14th aka 3/14 or 3.14 is known as $\pi$ Day, and it honors the mathematical constant $\pi$ (pi), which represents the ratio of a circle's circumference to its diameter. On this special day, I wanted to dedicate a blog post to the beauty of mathematics, numerical methods, $\pi$, and Mojo. So join me on this journey as I implement a fast vectorized Monte Carlo approximation method of calculating $\pi$. Happy $\pi$ Day!

### Approximating Pi

There are several methods for approximating $\pi$ including several Monte Carlo methods. Monte Carlo methods rely on repeated random sampling to obtain numerical results and rely on computation power to provide approximate solutions when solutions are hard to analyze mathematically. For this blog post, I chose one of the simplest Monte Carlo methods for approximating $\pi$.Â

Consider a circle inscribed in a unit square, the ratio of the area of the square to area of the circle is ${\pi}/4$:

$\frac{Area-of-circle}{Area-of-square} = \frac{\pi * (0.5)^2}{1*1} = \frac{\pi}{4}$

To approximate the areas we can repeatedly sample from an uniform random distribution x, y $\in$ [-0.5,0.5] and place these dots on the square. The ratio of dots inside the circle to the total number of dots will approximately equal Ï/4. To calculate $\pi$ you have to calculate:

$4*\frac{dots-inside-circle}{dots-inside-square}$.

As you increase the number of dots the approximation for Pi gets closer to the true value, and you can see this in the animation at the top.

### Approximating Pi in Mojoð¥

My goal with this blog post is to introduce you to some Mojo features as we implement this approach. The code for this blog post is available on GitHub. Iâll be building on the sample implementation of MojoArray I used in the Valentineâs day blog post. The core logic for pi calculation is as follows:

- Randomly sample x and y coordinates from a uniform random distribution with upper and lower limits = [-0.5,0.5]

- Calculate the magnitude of each dot using L2 norm

- Get a list of dots whose magnitudes are less than equal to 0.5, which is the radius of the circle

- Calculate $\pi$ with the formula $4*\frac{sum(inside-dots)}{N}$

The code for these steps are here:

For the above code to work, we have to add few additional functionality to MojoArray we implemented in the Valentineâs day blog post:

- Get boolean array from greater than and less than equal operations using __gt__() and __le__() and convert this into float array so we can perform arithmetic operations on it

- Support sum reduction, implemented in sum()

### âLess than equal toâ operations

Here I have a vectorized implementation of less than equal operations using SIMD types, that calculates the boolean response and then maps it into the default float representation of MojoArray (Float64). We need the float representation as the output to do a reduction and get the total number of dots in the next step. Note: I could have also used an integer representation, which will save some memory, but I chose the easy way out by using the default dtype MojoArray was initialized with.

Â For __le__() the crux of the logic is in the following line:

In this line of code, we are operating on vectors of SIMD width for Float64 to speed up computation. First we load the vector and then do a âless than or equal toâ comparison to return a boolean vector which contains True if itâs less than equal to val or False if itâs not. Then we use the select function to map the boolean vector to a floating point vector 1.0 (iters+1) or a 0.0 (iters) and save it in the new bool_float_array. We wrap this code in a closure and call vectorize which will automatically apply the operation to the entire MojoArray.

### Calculating the sum reduction

After we have the floating point representation of the boolean array, we have to reduce it to a scalar by adding all the 1s to count the number of dots inside the circle. We do this in a vectorized way in the following function:

We allocate a sum vector of simd_width to save the intermediate sums, and in the vectorize_reduce function, we perform vectorized additions. Again, we wrap this code in a closure and call vectorize which will automatically apply the operation to the entire MojoArray.Â

Finally we reduce the final vector s using the reduce_add() method.

### Calculating Pi

Now weâre ready to taste some Pie ð¥§ð, i mean Pi $\pi$. With our MojoArray data structure in place, weâre now ready to calculate Pi.

- Define a large sample size with N

- In a loop calculate Pi for different values of i from 0 to N in steps of 1000. For each i:

- Create MojoArrays for x and y

- Calculate radius r using L2 norm (aka euclidean distance, aka pythagoras theorem)

- Get a list of inside dots that are <= 0.5

- Calculate pi

- Plot results using Python module Matplotlib

- Loop till you reach N

The code for the main loop is here:

And the resulting animation:

### Conclusion

Hope you enjoyed reading this blog post on how to calculate Pi in Mojo using Monte Carlo method. What better way to celebrate Pi day than by delving into the implementing approximation of pi while geeking out on Mojoð¥.Â You can find all the full implementation on GitHub.

While the implementation was elementary, my hope is that you can reuse some of the learnings in your own projects using Mojo. Download Mojo to run this example and share your feedback with us! Happy Pi Day!Â

Here are some additional resources to get started.Â

- Get started with downloading Mojo

- Head over to the docs to read the programming manual and learn about APIs

- Explore the examples on GitHub

- Join our Discord community

- Contribute toÂ  discussions on the Mojo GitHub

- Read and subscribe to Modverse NewsletterÂ

- Read Mojo blog posts, watch developer videos and past live streams

- Report feedback, including issues on our GitHub tracker

Until next timeð¥!

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Developer

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Shashank Prasanna

AI Developer Advocate

Shashank is an engineer, educator and doodler. He writes and talks about machine learning, specialized machine learning hardware (AI Accelerators) and AI Infrastructure in the cloud. He previously worked at Meta, AWS, NVIDIA, MathWorks (MATLAB) and Oracle in developer relations and marketing, product management, and software development roles and hold an M.S. in electrical engineering.



================================================================================

URL: https://www.modular.com/newsletters

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# Modverse Newsletter

## Latest Issues

ð¨

NEW

Modverse Weekly - Issue 41

August 16, 2024

ð¨

NEW

Modverse Weekly - Issue 40

August 1, 2024

## Sign up for our newsletter, Modverse

Get all our latest news, announcements and updates delivered directly to your inbox. Unsubscribe at anytime.

Thank you for your submission.

Your report has been received and is being reviewed by the Sales team. A member from our team will reach out to you shortly.

Thank you,

Modular Sales Team

# All Modverse Issues (X)

ð¨

NEW

### Modverse Weekly - Issue 41

The Mojo Community Meeting has been renamed to the MAX + Mojo Community Meeting. We explained how we combined Mojo and MAX, and updated the license for both to ensure you can be confident building free (and open source) technologies, as well as using Mojo and MAX for your own commercial projects.

August 16, 2024

/

Jack Clayton

,Â

ð¨

NEW

### Modverse Weekly - Issue 40

Last week we began alpha testing our new magic CLIÂ for installing Mojo and MAXÂ through the Conda ecosystem. It allows you to pin Mojo/MAXÂ to different versions, and add Python dependencies so you can reliably share your Mojo and MAXÂ projects. If you want to try it out before release, you can find the doc for installing it here.

August 1, 2024

/

Jack Clayton

,Â

ð¨

NEW

### Modverse Weekly - Issue 39

Check out the new section for nightly updates. If you're not keeping track on the #nightly Discord channel, you can catch up on everything new for the last week in this newsletter.

July 9, 2024

/

Jack Clayton

,Â

ð¨

NEW

### Modverse Weekly - Issue 38

The 24.4 release introduced GGUFÂ support and quantisation for models such as Llama3 and many new features for Mojo. With that behind us Modular is now working hard towards the next release. To try out the new features press the Nightly tab on the MAXÂ installation guide. There's an active community in the #nightly Discord channel to discuss the upcoming features with, and Modular staff are there to answer questions and respond to your feedback!

June 28, 2024

/

Jack Clayton

,Â

ð¨

NEW

### Modverse Weekly - Issue 37

We hosted the third Mojo Community Meeting this week, with great demonstrations from the community on constraints and the Mojo HTTP serving library lightbug. We also previewed our upcoming AIÂ pipeline serving feature with llama3, using a GUI to interact with it. And ended by answering community questions on async, and when Python will be able to call Mojo.

June 20, 2024

/

Jack Clayton

,Â

ð¨

NEW

### Modverse Weekly - Issue 36

This week we released Mojoð¥ and MAXâ¡ï¸Â 24.4, there is too much in the release to cover here! Check out the launch blog, the Mojo changelog, and the MAXÂ changelog.

June 7, 2024

/

Jack Clayton

,Â

ð¨

NEW

### Modverse Weekly - Issue 35

This week we held the first ever Mojo community meeting! Many topics were discussed, including priorities, diving into specific features like async, and topics set on the agenda by the wider Mojo community. Next week we'll be hearing from community members about the awesome projects they've been working on. Make sure to add the next community meeting to your calendar.

May 23, 2024

/

Jack Clayton

,Â

ð¨

NEW

### Modverse Weekly - Issue 34

This week we launched MAXâ¡ï¸ nightlies with preview support for macOS and MAXÂ Serving! You can now find details about how to install it on modul.ar/get-started, press the Nightly button on the install guide if you want to be on the bleeding edge. We look forward to your feedback, join the Discord and leave comments on the #nightly channel.

May 17, 2024

/

Jack Clayton

,Â

ð¨

NEW

### Modverse Weekly - Issue 33

This week we hosted a livestream with many good questions from the community. People were most interested in macOS support for MAX, and GPUÂ support. These are both top priority at Modular so stay tuned! The standard library team were happy to announce that we've had 250 OSS contributions since open sourcing, we continue to be impressed and motivated by our active community!

May 10, 2024

/

Jack Clayton

,Â

ð¨

NEW

### Modverse Weekly - Issue 32

Yesterday marked Mojoð¥'s first birthday ð, launching last year on May 2, 2023. We launched with just an online Jupyter environment and have since released the SDK locally for macOS and Linux on arm64 and x86, and bought up our inference and serving stack MAXâ¡ï¸. Yesterday we released Mojo and MAX 24.3 to mark the occasion, along with the first community contributions, 32 of which made it into the official changelog. We're so excited to go on this journey out in the open with our amazing community, thank you to everyone who has been participating!

May 3, 2024

/

Jack Clayton

,Â

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/team/shashank-prasanna

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?author=Shashank+Prasanna

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/team/jack-clayton

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/team/walter-erquinigo

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?author=Jack+Clayton

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?author=Walter+Erquinigo

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/team/chris-hoge

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?author=Chris+Hoge

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog/max-graph-api-tutorial

- Home

- MAX

- Mojo

- Modular CLI

- Bring your own fine-tuned model to MAX pipelines

- Deploy a model with Kubernetes and Helm

- Deploy a model with Amazon SageMaker and AWS CloudFormation

- Get started with MAX Graph

- Create a custom op for an ONNX model

- Run an ONNX model with Python

- MAX

- Tutorials

- Get started with MAX Graph

# Get started with MAX Graph

Updated: Aug 14, 2024



MAX Engine is a next-generation compiler and runtime library for running AI
inference. With support for PyTorch (TorchScript), ONNX, and native Mojo
models, it delivers low-latency, high-throughput inference on a wide range of
hardware to accelerate your entire AI workload. As highlighted in the recent
MAX version 24.3
release,
the MAX platform enables users to fully leverage the capabilities of the MAX
Engine by creating bespoke inference models using the MAX Graph
APIs. The Graph API offers a low-level
programming interface for constructing high-performance symbolic computation
graphs in Mojo. This interface provides a uniform representation of
symbolic
values and a suite of operators that process these symbols to construct the
entire graph.

In this tutorial, we guide you step-by-step how to use the MAX Graph API. In a
nutshell, working with MAX Graph API involves three main steps:

- Building and verifying the graph.

- Creating an inference session and compiling the graph.

- Executing the graph with input(s) and retrieving the output(s).

We begin by creating two straightforward graphs for addition and matrix
multiplication in Mojo, demonstrating how to compile and execute these graphs.
Then we proceed to implement a two-layer feedforward neural network with ReLU
activation for
inference on MNIST data, comparing the accuracy to a PyTorch implementation.
Additionally, we implement
ReLU6 as a
custom operator and use the MAX Graph Custom Operator
API to
substitute ReLU and ensuring the accuracy aligns with the PyTorch model.

The code for this tutorial is on
GitHub.
The MAX version for this tutorial is the nightly (tested version max 2024.7.1505 (ba28802f)) for now.

If you experience any issues in this tutorial, please let us know on
GitHub.

## Set up MAX​

If you don't have MAX yet, follow the MAX install guide.

If you're new to the Mojo language, you can learn the basics in the
Introduction to Mojo.

If you have any questions along the way, ask them on our Discord
channel or in the GitHub discussions on
the Mojo repo and MAX
repo. Should you encounter any
issues, we recommend checking the roadmap and known issues
first.

## Build a "Hello, world!" graph​

To begin familiarizing ourselves with the Graph API, we start by constructing a
simple addition graph. We will verify and compile this graph, and then proceed
to execute it.

Below is a straightforward graph that takes two inputs; input0 and input1.
It adds these inputs together and produces output0 as the output.



### 1. Build the graph​

To construct the addition graph, we start by importing the necessary modules.
We then instantiate the
Graph by
specifying two input types of fixed static dimension 1 (we will later see
other types of supported dimensions such as symbolic dimension). Next, we
create a symbolic representation of the addition with the expression out =
graph[0] + graph[1]. Here graph[0] refers to the first input input0
and graph[1] to input1. This operation adds two inputs together. Finally,
we designate out as the output of the graph by calling graph.output(out).

```mojo
from max.graph import Graph, TensorType, Typegraph = Graph(in_types=List[Type](TensorType(DType.float32, 1), TensorType(DType.float32, 1)))out = graph[0] + graph[1]graph.output(out)print(graph)
```

We can print the graph to visually confirm its structure. The output should
show the following representation where rmo and mo are Modular’s internal
intermediate representations

```mojo
%0 = rmo.add(%arg0, %arg1) : !mo.tensor<[1], f32>, !mo.tensor<[1], f32>
```

This line corresponds to the symbolic addition operation out = graph[0] + graph[1].

The subsequent line

```mojo
mo.output %0 : !mo.tensor<[1], f32>
```

indicates that %0 has been set as the output of the graph, aligning with the graph.output(out) in our code.

The complete graph representation looks like this:

```mojo
graph: module {  mo.graph @graph(%arg0: !mo.tensor<[1], f32>, %arg1: !mo.tensor<[1], f32>) -> !mo.tensor<[1], f32> no_inline {    %0 = rmo.add(%arg0, %arg1) : !mo.tensor<[1], f32>, !mo.tensor<[1], f32>    mo.output %0 : !mo.tensor<[1], f32>  }}
```

To programmatically verify the complete graph construction, we use the
graph.verify() method. This checks for various structural integrity criteria
such as ensuring there are no cycles within the graph (acyclicity) which would
indicate recursion or feedback loops that can not be part of the dataflow
graph. For more details, check out the official documentation on the
verify
method.

### 2. Create inference session, load and compile the graph​

With our graph now verified and ready, the next step involves creating an
inference
session
instance, loading the graph into this session and compiling the graph into a
model
instance. We also print the input names to use when executing the model.

```mojo
from max import enginesession = engine.InferenceSession()model = session.load(graph)print("input names are:")for input_name in model.get_model_input_names():    # Mojo lesson: `[]` dereferences in Mojo as `input_name` is of `Reference` type    print(input_name[])
```

which outputs

```mojo
input names are:input0input1
```

Verifying input names input0 and input1 is crucial for correctly executing
the model in the subsequent section.

### 3. Execute the graph/model with inputs​

To execute the graph, we first create two input
tensors in Mojo,
specifying their names and values in the
execute
method. The result from the execution are returned as
TensorMap,
from which we can retrieve the value of output0 via the
get
method as follows

```mojo
from tensor import Tensorprint("set some input values:")input0 = Tensor[DType.float32](List[Float32](1.0))print("input0:", input0)input1 = Tensor[DType.float32](List[Float32](1.0))print("input1:", input1)print("obtain the result using `get`:")# Mojo lesson: here the `^` in `input0^` passes the ownership and ends the lifetime of `input0`ret = model.execute("input0", input0^, "input1", input1^)print("result:", ret.get[DType.float32]("output0"))
```

The outputs are printed as follows

```mojo
set some input values:input0: Tensor([[1.0]], dtype=float32, shape=1)input1: Tensor([[1.0]], dtype=float32, shape=1)obtain the result using `get`:result: Tensor([[2.0]], dtype=float32, shape=1)
```

Now, let’s explore our second example.

## Build a matmul graph​

In this example, we create a graph specifically for performing matrix
multiplication (matmul) by a constant symbol which we will use further along
in the next section. This type of graph is particularly important as it
demonstrates how constant symbols, representing trained and fixed weights in a
neural network, can be utilized. This concept will be expanded upon in
subsequent sections.



The setup for this matmul graph follows the same foundational steps as our
initial example but includes some critical additions:

- We introduce a
symbolic dimension
m to represent m x 2

- The use graph.constant to create  a constant symbol, crucial for
maintaining static values

Here's how we compile and execute the graph to accommodate varying input tensor
sizes at runtime:

```mojo
from max.graph import Graph, TensorTypefrom max.tensor import Tensor, TensorShapefrom random import seedfrom max.engine import InferenceSessiongraph = Graph(TensorType(DType.float32, "m", 2))# create a constant tensor value to later create a graph constant symbolconstant_value = Tensor[DType.float32](TensorShape(2, 2), 42.0)print("constant value:", constant_value)# create a constant symbolconstant_symbol = graph.constant(constant_value)# create a matmul nodemm = graph[0] @ constant_symbolgraph.output(mm)# verifygraph.verify()# create session, load and compile the graphsession = InferenceSession()model = session.load(graph)# generate random inputseed(42)input0 = Tensor[DType.float32].randn((2, 2))print("random 2x2 input0:", input0)ret = model.execute("input0", input0^)print("matmul 2x2 result:", ret.get[DType.float32]("output0"))# with 3 x 2 matrix inputinput0 = Tensor[DType.float32].randn((3, 2))print("random 3x2 input0:", input0)ret = model.execute("input0", input0^)print("matmul 3x2 result:", ret.get[DType.float32]("output0"))
```

Here are the results of matmul graph using a constant symbol of 2 x 2
tensor and a random input tensors of shapes 2 x 2 or 3 x 2 for
demonstration

```mojo
constant value: Tensor([[42.0, 42.0],[42.0, 42.0]], dtype=float32, shape=2x2)random 2x2 input0: Tensor([[-1.7141127586364746, 0.057178866118192673],[0.75628399848937988, -1.6024507284164429]], dtype=float32, shape=2x2)matmul 2x2 result: Tensor([[-69.591224670410156, -69.591224670410156],[-35.53900146484375, -35.53900146484375]], dtype=float32, shape=2x2)random 3x2 input0: Tensor([[1.0167152881622314, -0.10449378937482834],[-0.27936717867851257, -0.69003057479858398],[0.80745488405227661, -0.48231619596481323]], dtype=float32, shape=3x2)matmul 3x2 result: Tensor([[38.313301086425781, 38.313301086425781],[-40.714706420898438, -40.714706420898438],[13.655824661254883, 13.655824661254883]], dtype=float32, shape=3x2)
```

With this foundation, we are ready to explore more advanced applications in the
next section of the tutorial.

## Build an MNIST classifier graph​

In this section, we demonstrate how to build a two-layer neural network with
ReLU activation using PyTorch, train it on the famous MNIST
data featuring black and white
28 x 28 pixel images of handwritten digits (0 to 9 i.e. total of 10
classes) and then test its accuracy.



Subsequently, we will implement the same model using the MAX Graph API for
inference to ensure the accuracy remains consistent.

### 1. Build and train the model in PyTorch​

First, to set up, let’s define our neural network in PyTorch:

```mojo
import torch.nn as nnclass Model(nn.Module):    def __init__(self, input_size, hidden_size, num_classes):        super().__init__()        self.fc1 = nn.Linear(input_size, hidden_size)        self.relu = nn.ReLU()        self.fc2 = nn.Linear(hidden_size, num_classes)    def forward(self, x):        x = self.fc1(x)        x = self.relu(x)        x = self.fc2(x)        return x
```

We can train and test the network as follows (python mnist.py)

```mojo
loss_fn = nn.CrossEntropyLoss()optimizer = optim.RMSprop(model.parameters(), lr=learning_rate)total_steps = len(train_loader)for epoch in range(num_epochs):    for i, (images, labels) in enumerate(train_loader):        images = images.reshape(-1, 28 * 28)        outputs = model(images)        loss = loss_fn(outputs, labels)        optimizer.zero_grad()        loss.backward()        optimizer.step()        if (i+1) % 100 == 0:            print (f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{total_steps}], Loss: {loss.item():.4f}')# testmodel.eval()with torch.no_grad():    correct = 0    total = 0    for images, labels in test_loader:        images = images.reshape(-1, 28 * 28)        outputs = model(images)        probs = F.softmax(outputs, dim=1)        predicted = torch.argmax(probs, 1)        total += labels.size(0)        correct += (predicted == labels).sum().item()    print(f"Accuracy of the network on the 10000 test images: {100 * correct / total} %")# save weights in numpy binary formatweights = {}for name, param in model.named_parameters():    weights[name] = param.detach().cpu().numpy()np.save(f"model_weights.npy", weights)
```

After training and testing the network, we found the model achieves an accuracy
of 97.31% on the test dataset.

```mojo
Epoch [1/5], Step [100/469], Loss: 0.5384Epoch [1/5], Step [200/469], Loss: 0.2288Epoch [1/5], Step [300/469], Loss: 0.3225Epoch [1/5], Step [400/469], Loss: 0.2614Epoch [2/5], Step [100/469], Loss: 0.1049Epoch [2/5], Step [200/469], Loss: 0.2166Epoch [2/5], Step [300/469], Loss: 0.2362Epoch [2/5], Step [400/469], Loss: 0.1472Epoch [3/5], Step [100/469], Loss: 0.1200Epoch [3/5], Step [200/469], Loss: 0.1284Epoch [3/5], Step [300/469], Loss: 0.0726Epoch [3/5], Step [400/469], Loss: 0.1111Epoch [4/5], Step [100/469], Loss: 0.0702Epoch [4/5], Step [200/469], Loss: 0.0650Epoch [4/5], Step [300/469], Loss: 0.1297Epoch [4/5], Step [400/469], Loss: 0.1334Epoch [5/5], Step [100/469], Loss: 0.0265Epoch [5/5], Step [200/469], Loss: 0.0301Epoch [5/5], Step [300/469], Loss: 0.1179Epoch [5/5], Step [400/469], Loss: 0.0424Accuracy of the network on the 10000 test images: 97.31 %
```

Next, we implement the PyTorch model in MAX Graph API for inference.

### 2. Build the inference graph with MAX Graph​

After training our model and saving its weights, we need to construct an
inference graph and load the weights as constant symbols. Our graph will handle
input dimensions with a symbolic "batch" dimension and static 28x28 spatial
dimensions, representing flattened and preprocessed images. We will also
include a softmax operation via
ops.softmax
to compute probabilities directly within the inference graph.

```mojo
from max.graph import Graph, TensorType, opsfrom max import enginedef build_mnist_graph(    fc1w: Tensor[DType.float32],    fc1b: Tensor[DType.float32],    fc2w: Tensor[DType.float32],    fc2b: Tensor[DType.float32],    ) -> Graph:    # Note: "batch" is a symbolic dim which is known ahead of time vs dynamic dim    graph = Graph(TensorType(DType.float32, "batch", 28 * 28))    # PyTorch linear is defined as: x W^T + b so we need to transpose the weights    fc1 = (graph[0] @ ops.transpose(graph.constant(fc1w), 1, 0)) + graph.constant(fc1b)    relu = ops.relu(fc1)    fc2 = (relu @ ops.transpose(graph.constant(fc2w), 1, 0)) + graph.constant(fc2b)    out = ops.softmax(fc2) # adding explicit softmax for inference prob    graph.output(out)    graph.verify()    return graph
```

With the inference graph defined, we can now execute it with test images.

### 3. Run inference and check accuracy​

To execute the graph, we first convert the model weights from numpy format to
Mojo tensor format, then create the graph, compile it, and run inference.
Finally, to check the accuracy, we iterate on test images, preprocess them,
obtain the result and calls
argmax to
find the predicted value between the 10 classes and count how many of them
correctly match the ground truth label.

```mojo
weights_dict = load_model_weights()fc1w = numpy_to_tensor[DType.float32](weights_dict["fc1.weight"])fc1b = numpy_to_tensor[DType.float32](weights_dict["fc1.bias"])fc2w = numpy_to_tensor[DType.float32](weights_dict["fc2.weight"])fc2b = numpy_to_tensor[DType.float32](weights_dict["fc2.bias"])mnist_graph = build_mnist_graph(fc1w^, fc1b^, fc2w^, fc2b^)session = engine.InferenceSession()model = session.load(mnist_graph)correct = 0total = 0# use batch size of 1 in this exampletest_dataset = load_mnist_test_data()for i in range(len(test_dataset)):    item = test_dataset[i]    image = item[0]    label = item[1]    preprocessed_image = preprocess(image)    output = model.execute("input0", preprocessed_image)    probs = output.get[DType.float32]("output0")    predicted = probs.argmax(axis=1)    label_ = Tensor[DType.index](TensorShape(1), int(label))    correct += int(predicted == label_)    total += 1print("Accuracy of the network on the 10000 test images:", 100 * correct / total, "%")
```

The output of mojo mnist.mojo is

```mojo
Accuracy of the network on the 10000 test images: 97.310000000000002 %
```

This matches the accuracy we observed from the PyTorch test, confirming that
our MAX Graph API implementation performs equivalently.

## Create a custom operator with MAX Graph​

In this final section of our tutorial, we demonstrate how to create and
register a custom operator to use inside a MAX graph. Following our previous
two layer neural network, we first train our model with ReLU6 activation via
python mnist.py —-use-relu6 which replaces ReLU with ReLU6, checks the
test accuracy and saves the model weights that were done before.

### 1. Implement the custom op​

To create a custom operator in Mojo, we should follow these steps

- Create a dedicated sub-repository and name it custom_ops

- Create a __init__.mojo with the import content from
.relu6 import relu6

Create a custom op Mojo file, relu6.mojo with the following code

```mojo
from max.extensibility import Tensor, empty_tensorfrom max import register@register.op("relu6")fn relu6[type: DType, rank: Int](x: Tensor[type, rank]) -> Tensor[type, rank]:    var output = empty_tensor[type](x.shape)    @always_inline    @parameter    fn _relu6[width: Int](i: StaticIntTuple[rank]) -> SIMD[type, width]:        var val = x.simd_load[width](i)        return val.max(0).min(6)    output.for_each[_relu6]()    return output^
```

Above code uses @register.op(“relu6”) decorator to register the wrapped
relu6 function with name ”relu6”, as a custom operator. The wrapped
function can only take max.extensibility
tensors
and must have only one output of the same type and can not raise an
Error. We create an
empty_tensor
to store the output.

To obtain the output, we create a function wrapped in @parameter to be
applied on each element of the input tensor via
for_each.
Such function (_relu6) loads SIMD values of each rank and applies the
ReLU6 formula val.max(0).min(6). Finally, we move the output
via output^ to correctly transfer ownership of the result tensor.

### 2. Add the custom op to the graph​

Once we have the custom operator defined, we need to package it as .mojopkg
via mojo package custom_ops.

In our graph definition, we are now ready to replace the ops.relu with our
custom one

```mojo
relu = ops.relu(fc1)
```

with

```mojo
relu = ops.custom["relu6"](fc1, fc1.type())
```

Here we use the
ops.custom
that takes the custom operator name ”relu6” as parameter and the fc1 as
input and the output type fc1.type(). The rest of the code stays the same.

```mojo
def build_mnist_graph(    fc1w: Tensor[DType.float32],    fc1b: Tensor[DType.float32],    fc2w: Tensor[DType.float32],    fc2b: Tensor[DType.float32],    use_relu6: Bool    ) -> Graph:    # Note: "batch" is a symbolic dim which is known ahead of time vs dynamic dim    graph = Graph(TensorType(DType.float32, "batch", 28 * 28))    # PyTorch linear is defined as: x W^T + b so we need to transpose the weights    fc1 = (graph[0] @ ops.transpose(graph.constant(fc1w), 1, 0)) + graph.constant(fc1b)    # custom op    relu = ops.custom["relu6"](fc1, fc1.type())    fc2 = (relu @ ops.transpose(graph.constant(fc2w), 1, 0)) + graph.constant(fc2b)    out = ops.softmax(fc2) # adding explicit softmax for inference prob    graph.output(out)    graph.verify()    return graph
```

The last change is to let the inference session know about the custom
operator at runtime via

```mojo
model = session.load(mnist_graph, custom_ops_paths=Path("custom_ops.mojopkg"))
```

### 3. Verify the results​

As the final check, we train and test the model that uses ReLU6 via python
mnist.py —-use-relu6 which outputs

```mojo
Accuracy of the network on the 10000 test images: 95.91 %
```

Then we run the inference code via mojo mnist.mojo —-use-relu6 which shows

```mojo
Accuracy of the network on the 10000 test images: 95.909999999999997 %
```

The matching accuracy between the PyTorch version and the Mojo implementation
confirms the effective integration of the custom operator.

### 4. Deploy as a binary​

For deployment, we can build the mnist binary via mojo build mnist.mojo.
We can execute the binary as follows

```mojo
./mnist# or
```

```mojo
./mnist --use-relu6
```

## Next steps​

In this tutorial, we demonstrated how to use MAX Graph API step-by-step, to
create a symbolic graph, compile and execute such graphs. We also showed how to
replicate a two layer neural network trained in PyTorch, in MAX Graph API and
saw that the test accuracy remained intact. We concluded by showing how to
create and register a custom operator to use for inference. To verify
correctness, we showed the test accuracy also remained intact when using such a
custom operator. We hope that by the end of this tutorial, you have gained a
better understanding of the inner workings of MAX Graph APIs.

Here are a few potential steps for you:

- Explore other neural network architectures beyond a simple two-layer
feed-forward network and implement them using MAX Graph API

- Experiment with other custom operator

- Test and assess correctness and contribute to the community 🚀

Report feedback, including issues on our
Mojo and
MAX GitHub tracker.

- Set up MAX

- Build a "Hello, world!" graph1. Build the graph2. Create inference session, load and compile the graph3. Execute the graph/model with inputs

- 1. Build the graph

- 2. Create inference session, load and compile the graph

- 3. Execute the graph/model with inputs

- Build a matmul graph

- Build an MNIST classifier graph1. Build and train the model in PyTorch2. Build the inference graph with MAX Graph3. Run inference and check accuracy

- 1. Build and train the model in PyTorch

- 2. Build the inference graph with MAX Graph

- 3. Run inference and check accuracy

- Create a custom operator with MAX Graph1. Implement the custom op2. Add the custom op to the graph3. Verify the results4. Deploy as a binary

- 1. Implement the custom op

- 2. Add the custom op to the graph

- 3. Verify the results

- 4. Deploy as a binary

- Next steps

- 1. Build the graph

- 2. Create inference session, load and compile the graph

- 3. Execute the graph/model with inputs

- 1. Build and train the model in PyTorch

- 2. Build the inference graph with MAX Graph

- 3. Run inference and check accuracy

- 1. Implement the custom op

- 2. Add the custom op to the graph

- 3. Verify the results

- 4. Deploy as a binary

- What is MAX

- Install MAX

- Mojo Manual

- Changelog

- Sign up

- Company

- Blog

- Careers

================================================================================

URL: https://www.modular.com/contact/sales

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog/leveraging-max-engines-dynamic-shape-capabilities

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

March 28, 2024

# Leveraging MAX Engine's Dynamic Shape Capabilities

Mikhail Zolotukhin

Ehsan M. Kermani

In this blog post, we will dive deep into the dynamic shapes support in MAX Engineâs 24.2 release. We will do so by first defining what dynamic shapes mean in machine learning, discussing their various types and use cases. We will then see how to use dynamic shapes in MAX Engine. Finally, for demonstration purposes, we will compare the average latency of the dynamic shapes to the static shapes for the BERT model on the GLUE dataset.

## What are dynamic shapes?

In machine learning, dynamic shapes refer to the ability of models to handle inputs of various sizes automatically. This capability is essential for managing real-world data, which often varies in size and dimension. This includes processing input text of unknown length - a common situation in applications such as chatbots - or dealing with batches of inputs whose sizes are not known in advance (dynamic batching).

## What are the types of dynamism in AI models?

In AI models, dynamic shapes can manifest in several ways depending on the degree of âdynamismâ involved:

- At the most basic level, tensor shapes can be entirely static, meaning their rank (the number of dimensions), data type, and dimensions are all specified in advance.

- Next, tensors might have a known rank, data type and sizes across some dimensions, but not all. For example, if a model processes images, the input tensor might be a 4-d tensor in an NCHW layout (Batch size, Channels, Height, Width). In this case dimension N can be dynamic to allow for varying batch sizes, H and W can be dynamic too if we expect the model to work on images of arbitrary sizes. However, C is often static - assuming all input images are expected to have the same number of channels (e.g. R, G, B for color images).

- Moving towards greater dynamism, tensors might have a known rank and data type, but unknown shapes across all dimensions. Continuing our previous example, consider a model that works with both colored and black-and-white images, making the C dimension (channels) also variable.

- If we increase the dynamism degree even more, we get rank-dynamic tensors - i.e. tensors, for which we donât know their rank, but still know their data type.

- Finally, in the most dynamic scenario, we might lack any information about the tensors and donât even know their data type.

As the level of dynamism increases, it becomes more and more difficult to optimize performance of the model. This is why most existing frameworks nowadays focus on handling the first three cases (1-3), where at least some tensor properties are known. Letâs examine that in more detail.

## What type of dynamism does MAX Engine support in 24.2 release?Â

As we explained various types of dynamism above, in the 24.2 release, MAX Engine supports dynamism across any dimension, but the rank and data type of the tensors need to be known. When MAX is used for running TensorFlow or ONNX models, this information is extracted directly from the model, and when we use a TorchScript model we require the user to provide such information explicitly since the model itself doesnât contain it. Similarly, MAX Graph API also allows specifying which dimensions are dynamic in the model.

## MAX Engine dynamic shapes support for PyTorch models

In this section, we will explain how to use MAX Engine for applications that require dynamic shapes. For the sake of simplicity, we will use the BERT model, but exactly the same technique is applicable to all other models including state-of-the-art Large Language Models.

Here, our model will process an input text, trying to guess a masked word in it. Because we donât know in advance how many words (or tokens, to be more precise) will be in the input, our model needs to be able to handle inputs of unknown shapes.

For PyTorch models, MAX Engine requires a TorchScript format of the model. Here is how to convert the BERT model to TorchScript by tracing a dummy input:

Next, we create an inference session for the MAX Engine and load our TorchScript model into it, providing the input information along with the model.

Note that instead of passing concrete sizes of the tensors below, we will pass None for each dimension that needs to be treated as dynamic:

None

Side note: Please refer to the relevant documentations if you are working with MAX using C API or Mojo API and learn how dynamic dimensions need to be specified.

The maxmodel is now ready for inference. As an example, we tokenize the input "Paris is the [MASK] of France.", then use maxmodel.execute to get the output and post-process the output to find the predicted_token as follows:

which outputs:

## Latency comparison of dynamic vs static shapes

For demonstration purposes, we compare here the average latency between dynamic shapes and static shapes for the BERT model on the GLUE dataset. With dynamic shapes our model processes only the input data and nothing else, which can be beneficial for datasets where the majority of samplesâ lengths are short. With static shapes, however, the inputs are padded to their maximum length which is 512 in the BERT example, which results in unnecessary computations and wasted resources.

## Conclusion

Throughout this post, we have explored the concept of dynamic shapes and the types of dynamism in AI models. The incorporation of dynamic shapes allows models to handle a wide array of input sizes, which is a common requirement in real-world scenarios ranging from text processing to image analysis. We have showcased MAX Engineâs dynamic shape support for the BERT model and then how to run inference for a sample input. At last, we have compared the average latency of the dynamic shapes to the static shapes for the BERT model on the GLUE dataset showing that on average dynamic shapes have lower latency compared to the static shapes. This is due to having shorter tensor lengths on average compared to longer tensor lengths for the static shapes case.

Additional resources:

- Get started with downloading MAX

- Download and run MAX examples on GitHubÂ

- Head over to the MAX docs to learn more about MAX Engine APIs and Mojo programming manualÂ

- Join our Discord communityÂ

- Contribute to discussions on the Mojo and MAX GitHub

Report feedback, including issues on our Mojo and MAX GitHub tracker.

Until next time!ð¥

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Developer

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Mikhail Zolotukhin

AI Compiler Engineer

Expert in traditional and ML compilers with over 15 years of experience in the field. Before Modular, Michael contributed to Intel Compiler, GCC, LLVM, and most recently PyTorch. He is passionate about building right things the right way and excited to do that at Modular to help power AI for the world.



Ehsan M. Kermani

AI DevRel

Ehsan is a Seasoned Machine Learning Engineer with a decade of experience and a rich background in Mathematics and Computer Science. His expertise lies in the development of cutting-edge Machine Learning and Deep Learning systems ranging from Natural Language Processing, Computer Vision, Generative AI and LLMs, Time Series Forecasting and Anomaly Detection while ensuring proper MLOps practices are in-place. Beyond his technical skills, he is very passionate about demystifying complex concepts by creating high-quality and engaging content. His goal is to empower and inspire the developer community through clear, accessible communication and innovative problem-solving. Ehsan lives in Vancouver, Canada.



================================================================================

URL: https://www.modular.com/team/joe-pamer

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog/meet-joe-pamer-mojo-engineering-lead

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

May 15, 2024

# What Does Joe Pamer, AI and PL expert, Want From Mojo?

Joe Pamer

Chris Lattner

Joe Pamer recently joined Modular to lead the Mojo engineering team. Joe is a veteran of Meta, Apple, and Microsoft and has led large scale infrastructure projects but can never seem to escape programming languages. We thought it would be fun to dive into Joeâs background and perspective and how he sees the opportunity ahead of Mojo and MAX and the industry at large.

Mojo is a new programming language that is a member of the Python family. Â It is as easy to use as Python, but is distinguished by its high performance and ability to scale into GPU programming and other specialized domains like AI.

Chris Lattner: Youâve made major contributions to a wide variety of different programming languages (PL) including F#, TypeScript, Swift, Hack, Python (etc!) that cross-cut static and dynamic languages. What have you learned across these journeys and what makes you excited about Mojo?

Joe Pamer: While I keep bouncing between opposite ends of the design spectrum, I've honestly never been particularly adamant about any of the âgreat debatesâ: static vs. dynamic, functional vs. imperative, etc. Lifeâs too short!

To me, an ideal programming language allows you to adapt your style and approach to the situation, and what attracted me to all of the ones Iâve worked on is that theyâve strived to balance opposing design constraints. I like meeting the programmer where theyâre at, and giving them new tools while youâre doing so. Maybe thatâs the most important lesson Iâve learned: the best designs are rarely found at one end of the spectrum or the other.

For example: whatâs better, static or dynamic? It depends! Static languages are great when you know exactly what you need to do, and want to do it in the most efficient, rigorous way possible - writing to the metal. Advanced typing features also help manage complexity as codebases scale. But sometimes you just need to experiment, sketch out a solution, orchestrate, or just hack. You donât need to frame your approach up-front; you want to explore and are willing to accept the necessary tradeoffs. When thatâs the case, when youâre figuring things out as you go, dynamic languages really shine.

Whatâs funny is that when you start with one you often begin to crave the other. Python is ubiquitous for the feature engineering and modeling parts of a pipeline, because it is great for experimentation and iteration. Â But then you get into production and performance always becomes an issue, so you have to switch to C++ or Rust. Â For serving or inference parts of a pipeline, C++ does make sense because you need extreme scale... but then your requirements change and its lack of flexibility means that you need to rewrite or throw everything away at some point.

Trying to find a balanced solution to the ML pipeline problem is what attracted me to Mojo: you can have it both ways, without compromises. It's like having the power of C++ and the expressiveness of Python elegantly fused together in a way that feels instinctive and powerful.

CL: Youâve scaled AI projects at Meta and are an expert in these systems. What are the biggest challenges youâve faced in the past, and how do you think MAX will be able to help?

The challenge of âbig AIâ is that youâre facing all the challenges of modern software development at scale while moving at tremendous pace and under tremendous market pressure. Weâre all surfing past the edge of the wave and coping best as we can, facing the same problems over and over again.

First, youâre up against the laws of physics. Everything is evolving so rapidly out from under you that your infrastructure is perpetually and fundamentally stressed. Compute resources, network capacity, energy capacity - youâre constantly bouncing against the very limits of whatâs possible with existing technology, pushing the envelope. Engineering for efficiency is key, but itâs so hard to manage across all of these different systems and the tradeoffs are tricky - itâs all zero sum.

Next, modern ML pipelines are massive heterogeneous systems, all running different software stacks on different hardware. C++ and Python werenât designed for this, and we have a crazy set of libraries and frameworks (PyTorch/Tensor-RT/CUBLAS/etc) - that try to paper over the problems. So youâre left to constantly battle this complexity, and every link in the chain is a potential point of failure. Even worse is that these are all opaque boxes, so when one of them breaks, you're stuck looking for a workaround instead of being able to fix things yourself.

Finally, modern AI is a multidisciplinary endeavor: you need ML engineers, software engineers, data scientists, and mathematicians to be able to work together, but they work very differently and often have contradictory needs. Traditional languages create a tooling gap for your team because they are literally speaking different languages, and cannot collaborate on a shared codebase that spans research to production. This complexity makes it more difficult to get AI into your products, and results in a slower innovation cycle.

I donât think these problems - efficiency, heterogeneity, and productivity - can be solved separately. The tools we currently use were designed for the last generation of problems, and their lack of cohesion is preventing us from moving forward. Thatâs what drew me to Mojo/MAX. Itâs tightly integrated, performant, and maximizes optionality at the upper and lower levels of the stack. It also allows whole classes of disparate developers to work their way, but using the same tools. It lets you get on with your life, and focus on building great AI products, not managing infrastructure.

CL: Where do you want to see Mojo in two years, how about ten years?

Mojo is at an exciting point in its development - itâs come so far in the past year, and is evolving rapidly. Â The community is exploding in size, and weâre seeing people build new things every day.

We want Mojo to live up to its potential, so we building out the core technology, rounding out even better tooling (including package management, a long-standing pain point for Python developers), and building Mojo into a scalable open ecosystem. While it is very important to us that Mojo be a good member of the Python family, Mojo is capable of scaling into many more use-cases, and we intend to continue growing the Mojo ecosystem on its own terms - just as C++ grew into a superset of C.

This last goal connects to my ten-year vision in that I donât see Mojo as âjustâ a language for AI - I think it can be great for many applications. Over the next decade I hope we will see Mojo expand into many domains beyond AI, with an identity all its own. Itâs already a joy to use and I only see us getting better and more compelling to a wider audience of developers over time.

One thing I know for certain: for either our short or long-term goals to happen, we need to foster a large and empowered community, and this is only possible through open source. Mojo has a lot of really innovative technology under the hood that weâre looking forward to sharing with the world. Â We recently open sourced the standard library, and our first Mojo Community Meeting is next week!

CL: As a âcity personâ youâve experienced both the SF and the NY scenes, why choose New York?

SF is a wonderful city and a great place to work, but NYC is âhomeâ to me and has always been close to my heart, so Iâm unabashedly biased about this one ð.

Iâd say the tech sector in NYC is pretty vibrant right right now, but it certainly wasnât always this way. I love that NYC has a diverse set of professions, instead of being a tech-centric monoculture like SF or even Seattle. This lack of a professional monoculture here really changes the way people in this field relate to their work. Combined with the typical NYC hustle, the tech scene here feels âricher,â more vibrant and more energetic in a way thatâs unique to the city.

One thing I'm very thankful for is that Modular is a remote-first company, which allows me to work from the city that I love. We already had an awesome group of engineers here (and up and down the east coast) when I joined, so it's never felt lonely either. I'm excited to keep growing out our presence here, and to contribute back to the local scene.

CL: What do you think about emojis in programming languages?

Naturally, I ð¤ they're ðï¸ if â¡ï¸ð¤ ð to ð ð°ï¸ â â¨ï¸ â©â±ï¸â¦ âï¸ ð®, too... LOL.

CL: Awesome, it is such an exciting time Joe. Thank you for driving Mojo to the next level!

Before we jump, I just wanted to mention that weâre investing even more in Mojo compiler, language, tooling, and building out the next generation of Mojo libraries. Â We recently posted a bunch of new roles, so if youâre interested in building the future for AI infrastructure and software, please check it out!

-Joe Pamer

- Learn more about the culture and values at Modular.

- Weâre hiring! Head over to our careers page to see open roles, and join us in building the future of AI!

- Get started with the MAX platform.

- Head over to the MAX docs to learn more about MAX Engine APIs and Mojo programming manual â

- Join our Discord community!

Until next time! ð¥

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Company

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Joe Pamer

Mojo Distinguished Engineering Lead

Joe spent the last 8 years at Meta, where he was focused on improving the companyâs AI and developer infrastructure stacks. Before that he served as Instagramâs CTO, overseeing much of Metaâs work on programming languages (mainly focused on Python, PHP, and Javascript), and co-founded its first applied ML team for infrastructure. Before joining Facebook, he helped design and develop the F#, TypeScript, and Swift programming languages, and contributed to many other developer technologies, such as Clang, Visual Studio, and VS Code.



Chris Lattner

Co-Founder & CEO

Distinguished Leader who founded and scaled critical infrastructure including LLVM, Clang, MLIR, Cloud TPUs and the Swift programming language. Chris built AI and core systems at multiple world leading technology companies including Apple, Google, SiFive and Tesla.

clattner@modular.com

================================================================================

URL: https://www.modular.com/blog/developer-voices-deep-dive-with-chris-lattner-on-mojo

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

May 8, 2024

# Developer Voices: Deep Dive with Chris Lattner on Mojo

Chris Lattner

Last week, Chris Lattner sat down for an interview on the Developer Voices podcast with Kris Jenkins. It was a wide-ranging episode that explored a variety of topics, including the motivations behind creating Mojo, what it offers to both Python and non-Python programmers alike, how it is built for performance, and which performance features actually matter. This post recaps a number of highlights from the podcast, edited for clarity and brevity. You can find the full 90 minute interview on YouTube.

### Why are you building a new language?

Summarized from 3:29 into the video

We started with how do we make GPUs go brrrr? How do we make these crazy high performance CPUs that have matrix operations, bfloat16 and other AI extensions. How do we rationalize this wide array of different hardware and put it into a system that we can program in? For hardware it used to be that a CPU would come out every year and it would be slightly better than the old one. But now we have these really weird dedicated hardware blocks which are very specialized. The most extreme is a GPU, being able to program these things requires fundamentally different kinds of software.

We weren't originally intending to build a language at Modular. We started building a very fancy code generator that had no frontend. We wrote everything using a new compiler framework named MLIR. It's now part of the LLVM family, but it's a next generation replacement in many ways. It allows making domain specific compilers really fast, and so we were writing everything in pure IR. We did that for quite some months to just prove out that the code generation philosophy in the stack could work and deliver the results we wanted. When we had confidence in that, we said "Now what do we do about syntax?"

In the case of Mojo we don't build an AST traditionally, we generate MLIR directly from the parser. When we decided it was working with a novel and useful code generational approach, we had to decide how are we going to get this IR. The obvious thing to reach for is a domain specific language embedded in some other language. For example Python, with a decorator based approach, generates IR by walking some other languages AST. The other approach is to go build a language. We decided let's do the hard thing, and we think the cost benefit trade off is worth it.

### Why build Mojo as a superset of Python?

Summarized from 11:49 into the video

What I realized is that I want to control some of the risk, but also meet developers where they are. For AI in particular, Python is "the thing", everybody uses Python. If you pick anything else, you'd have to justify why it's better than Python. I care about the hundreds of millions of developers who already know Python, not having to retrain them is a huge feature. From an engineering and a design perspective, it's very useful because we already know what the language looks like. So we don't have to bike shed, which is way easier than rationalizing everything using first principles. Mojo is a very extended version of Python, there's still design work, but at least we could focus our energy, as most of those early decisions are made for us.

In the case of Swift, we progressively migrated the Objective C community. We did that by making it so both worlds could live together. You can call one from the other happily, for example use an Objective C package, but write your UI in Swift. In the case of Mojo we're building it into a full superset of Python. And so all the Python idioms, whether they're a good idea or not will work in Mojo. Even today you can import arbitrary Python packages, mixing and matching them directly. We're continuing to make progress on more dynamic features in particular. Mojo will soon be a really good replacement for CUDA, we're close to being a really good replacement for languages like Rust, and eventually we'll be a good superset of what Python is loved for. But we have to do those steps incrementally and build out the features as we go.

Objective C and Python have the dark truth that all the important stuff is written in C or C++. If you look at NumPy as just one example, it has a very nice Python API, but it's all written in C, C++. The consequence of using Mojo that you don't have to switch out your Python knowledge. Programmers are busy people, they have things going on. Most people are not going to be learning a new thing for the fun of it. On the other hand you can meet people where they are, and provide something familiar so they don't have to retrain from scratch. Programmers I've seen generally love growing, and learning new tricks. That's why people are excited about what's going on with Mojo.

### How does Mojo's type system work?

Summarized from 20:02 into the video

If you ask the typical programmer, they would tell you Python doesn't have types. If you talk to a more advanced developer, they'd say it has dynamic types. And so there is a list, dict, string, but it's all runtime. If you go further you can say Python has one type, which is a reference to a Python object. In Mojo we said let's give this thing a name, which became PythonObject. Now you can create a Mojo type Int with conversions between PythonObject to have them integrate and interoperate. Now it can be on the stack instead of boxed and heap allocated, it can be the size of the word on your machine or whatever properties you need. And so you can have the ability to opt into a fully static world. CPU and GPU high performance numeric programmers never want anything dynamic. They want full control over the machine, and access to very fiddly low level optimizations. But we also allow you to start removing types, so you can get fully dynamic behavior if you'd like.

There's a whole class of languages, including TypeScript and even Python itself, where it's saying the fundamental nature of the universe is untyped. But you can provide type hints than can be used to inform error messages. Typing in Mojo is actually typed, this is very important. We've built a modern statically typed, generic, higher order functional type system â like many modern languages. It's pretty familiar to people and I think familiarity is good.

The other side of it though, is that we are very hardcore about pushing language features into the library. I'll pick on C++ here because it's an easier victim but it's a very powerful language. For example float and double are built into the language, so there's certain behavior and conversions that only work with builtins, which can't be exposed out to the library. Another example is you can't overload the dot operator in C++, you can't build a smart reference. That's been driving people nuts for decades now. In Mojo we take a very aggressive approach to this, which is to push everything that we can into libraries, and push the magic out of the compiler. For example Int is not built into the language, the float types are not built in the language, these are all just libraries and so the type system composes and we use it for all the builtin types. That forces the language to provide only the core essentials for expressing libraries. We want everything to be ergonomic and very dynamic and flexible and powerful for the Python folks.

Most people will build on top of higher level libraries. And so we have the MAX engine, which you to just run a graph if you don't want to know how any of that stuff works. here's a graph, go to town. Mojo allows you to add some types to go 100x or 1000x faster, without even doing fancy accelerator stuff. This is a material speedup for not having to retrain all your engineers. And so that's cool, even if you don't use the full power of what Mojo can do. You might not want to write GPU level code, you might just want your code to be fast. But when you want to go for performance, you can do that without someone telling you that you picked the wrong language.

### How do you deliver high performance and portable code?

Summarized from 26:34 into the video

Mojo believes in zero cost abstractions which are seen in C and Rust and many languages. The way we do it is pretty innovative because we have this fancy MLR compiler behind the scenes. This allows us to build up zero cost abstractions for various hardware. Swift in a way was syntactic sugar for LLVM, at the very bottom of the stack it could talk directly to LLVM primitives. Mojo does basically that same trick, but it supercharges it by moving to this MLIR world. And so MLIR being a much more modern compiler stack has way more powerful features.

We can expose things like bfloat16, and really weird numerics on accelerators like tiled matmuls, and the tensor core on a GPU, and then wrap them in really nice libraries. So you get direct low level access to crazy exotic hardware. But then you have syntactic sugar, built-in libraries, and now you can extend the language without having to be a compiler nerd. I think this is very important and powerful. So for example complex numbers are hardware accelerated on certain chips. If you do a multiply accumulate, it's four multiplies and a couple of adds, certain CPUs have operations that can do that in one shot. We have a compile time metaprogramming system that can say if you're on supported hardware go do the optimize, else just do the generic thing.

Mojo has superpowers because of the domain it's in. So you can write one piece of source code and that runs partially on your CPU, partially on your GPU. And these may have different pointer sizes and may have different numerics and capabilities. there are these very nerdy compiler things that enable this to just work in a way that people aren't quite used to. Mojo was designed in 2022 instead of 1985, we have things like SIMD which are core to the language, as all computers have SIMDÂ instructions these days.

We also have direct support for explicit vectorization, so you get full access to the hardware. It's been really fun seeing Mojo developers worldwide take something like game of life, and they say I'll start using this, I'll try this. Oh wow, it's a thousand times faster than the code I started with. With library based extensibility, our vectorized function is just a library function. And you can vectorize this code by using a combinator from the library, andÂ build it one step at a time. I've seen tons of people go through this growth path where they say wow, this is really cool, I'm having fun, I'm building something interesting, I'm learning something. This is where I think people love going through the growth path.

### How does Mojo work with GPUs and AI?

Summarized from 32:07 into the video

In the AI space I'm in love with AI for both the user applications, but also all the systems and all the technology that were built to support it. One of the things I found super inspiring is that today you can sign up for a Jupyter notebook on Google Cloud, and get access to a TPU. And with a few lines of code, you can now be programming on an exaflop supercomputer. You're describing a novel computation that gets mapped, partitioned, scaled out across thousands of chips, at massive data center speed. High Performance Compute (HPC) people have been doing for a long time. but now you have AI researchers doing this, without having to write low level high performance code.

What made that possible was a shift from completely imperative programming to declarative programming. And the way this works is that in AI, you build a machine learning graph and you have the AI researcher thinking about the level of ops like matrix multiplication, convolution, gather, or reduction etc. So they think about simple compositions of these highly parallel operators. And then you give this graph to a very fancy compiler stack, and it's actually doing fusion on the loop. You're taking this very complicated math, doing very high tech compiler transformations, and then also dealing with distribution across clusters. You can do these things because it's a declarative specification, you're not trying to take a pile of C code and parallelize it. What this whole stack evolves into in the case of Mojo is we have, what's called the MAX engine. The MAX engine is a very fancy AI compiler stack.

It's like a XLA, but after learning a lot of lessons being familiar with these technologies, it can run machine learning graphs. But we also want it to be able to talk to the imperative code. You need to be able to write custom operators, invent new algorithms. Like if Modular doesn't know what an FFT is, but you do and that's really important to your signal processing domain. We want the stack to be completely extensible. And so Mojo enables people to write an algorithm in very simple and familiar code. You can understand it because you're just writing source code. In contrast, CUDA is its own little world and it's very different to Python. So instead of writing a CUDA kernel, you can write some Mojo code and because of MLIR, it can reflect onto it and we can see what the code is doing.

That allows us to take it and do things like fancy compiler fusions, and do the placement. That's something the world doesn't have because in the AI space, the state of the art technologies are built around CUDA and math libraries like Intel, MKL where the operators are all black boxes. There are these fancy graph compiler things, but they don't actually have the ability to see into the logic that they're orchestrating, and so they can't do these high level transformations. It just is very janky in various ways. Part of our mission is to solve this, take all these systems a major step forward. What drives Mojo is the extreme high performance needs of AI. We want to be state of the art on performance without relying on vendor libraries that do matrix multiplications, for example. It is also why we care about usability, many people are building graphs with Python and PyTorch. And so we want to meet people where they are, it all flows together.

### How are you solving the AI divide between research and production?

Summarized from 37:19 into the video

The traditional TensorFlow and PyTorch libraries were designed 8 or 10 years ago, depending on how you count. They're coming from a research world and training world, but today a huge amount of focus has shifted to deployment. When you get into deployment mode you don't really want Python in production. It can be done, but there's some challenges with that. We've entered into this world with researchers who love, live, and breathe Python, and it's great for their use-case. But then you have production people that have to rewrite these models and tokenization logic for LLMs in C or Rust to be able to ship something. A big part of Mojo is about solving that problem, having one language that can scale which can heal the divide between all the personas that are building these systems. Whether they're high performance numerics people, deployment engineers, AI researchers, we can get everybody to be able to talk to each other, because they're literally speaking different languages which is massively impacting AI getting into production. It's an evolution of a lot of very well considered systems that were locally developed, aggregated and then hill climbed like crazy, because AI has changed a lot in the last five to eight years.

Nobody's had a chance to go back and first principles some of the technology, all this stuff grew really quickly. Some people might say that building a programming language is insane, but It's just a multi-year project. You have to be very practical about this and you have to make sure to not sign up for something you can't deliver on. It's a big bet that lots of other people aren't willing to make for a wide variety of reasons. And so you have to be right, but if you're right, then it's actually a really good contribution to the world.

An analogy I've seen is that it's like writing C or Rust code but with Python syntax. This is disarming to people because people are taught that Python is slow and to never write a for loop. In Mojo that wisdom is invalid, because it's not the same implementation. Many things that people knew to be false are actually totally fine. We have folks that from HPC backgrounds, experts in low level system architecture, saying it's so weird to be writing assembly code in Python. It does twist your brain, open your eyes and shift your perspective, but otherwise it's familiar. It isn't driven by novelty for novelty sake. It's about pragmatism.

### How does metaprogramming work, and how does it compare to Zig?

Summarized from 42:38 into the video

In the AI world source code is effectively a metaprogram. It's a bunch of imperative logic, that describes roughly a graph, you then distribute and transform it. Python has long been used for metaprogramming for a wide variety of different domains, that's one of the reasons it's been very successful in the AI community. If you look at high performance numerics, people often use C++ and templates for metaprogramming, because you want an algorithm that works on float, double, or float32/float64. And of course, then it turns in this massive cataclysm of templates depending on how advanced you get.

More modern languages like Zig for example, have said let's not have a different meta language than the language. They use the same language for both normal programming and metaprogramming. In case of Mojo we said that's actually a really fantastic idea. Python is highly dynamic, you can overload operators, you can do all these things dynamically, we can't pay the expense, we can't have even a single clock cycle extra in our domain. We need bare metal performance, but we want the benefit of the abstractions and the extensibility that Python provides. So we fused comptime metaprogramming and the dynamic metaprogramming of Python.

This is one of the major ingredients that allows Mojo to be extremely expressive with the same idea as Zig, you can build the standard runtime algorithms, you can allocate heap data structures, you can do all this stuff at compile time. This gives you a composition that enables really expressive libraries. It allows you to build combinators, higher level functions, and features, composing the benefit of the compiler world with the runtime world. You have values, objects, functions, features, classes, types, and you can use them either at compile time or run time. A simple example is a function that creates a lookup table. You can call it at runtime and pass dynamic values in as the arguments, doing all kinds of math on them. But you can also run it at compile time, calculate the dynamic data structure, do all the logic that would have run at runtime. The output of that is a list, that list is then burnt into the executable, and now you don't have to compute it at runtime.

That's a simple example, there are many fancier examples. Types are just values, and so your types can be compile time values. So you can do much more fancy, higher level programming. There's a whole rabbit hole there, the cool thing about is that it comes back to enabling library developers to make demand specific abstractions and build things that allow modelling their world very clearly. Zig has its own personality, it's a very low level language. It's very different in certain ways to Mojo. Mojo wants to enable libraries and abstractions and so that's its focus. But this idea of using the language at comptime is shared, we're very happy to learn from other people.

### How does memory management work, and how does it compare to Rust?

Summarized from 48:25 into the video

One of the ways that we can embrace the entire Python ecosystems, is interoping with the CPython object model. Everything is just compatible, if you import Python you get the traditional reference counted indirect object box. In Mojo native code you get a very powerful type system. At the bottom you have types with move constructors, copy constructors, and destructors. So you can write code that manages resources and do so directly. One of the bottom foundational things is that you can call into C. And so if you want to you can call malloc and free through unsafe hooks. But again, we want people to be able to compose together Libraries and we want to do so in a safe way. What we have is references with lifetimes that work very similarly to the way they work in Rust, there are many implementation differences, but you can think of it that way.

In Mojo, it's way less in your face, and you don't have to micromanage the borrow checker quite as much, but it provides you the same approach and the ability to manage references. This is a really powerful thing, and it's a very important thing, we've learned a lot from Rust. They paved a lot of roads with really great work, but there's certain challenges with the borrow checker. One example of that is the Rust parser has a bunch of pretty complicated rules, and special cases for how it generates the IR. Then you have the borrow checker which comes along and tells you if you did it wrong, the simple cases are easy to understand. But in the complicated cases, you're dealing with the order of evaluation of how the parser did things which causes all kinds of edge cases.

The Mojo equivalent is actually a very different thing. Our parser rules are very simple and predictable, we push a lot of complexity out of the language and into the library. Our borrow checker isn't just an enforcer, it decides what the lifetime of a value is. And so a very big difference between Mojo is that in Rust, values are destroyed at the end of a scope. You can run into issues where you get exclusivity violations because something lives too long, although there are various solutions to improve this like non-lexical lifetimes. In Mojo a value is destroyed immediately after its last use. This makes it a much more friendly experience because your lifetime ends and therefore exclusivity violations get relaxed much earlier. It's better for memory use, for example if you're talking to a GPU, a tensor could be holding on to four gigabytes of data, and so you want to free the memory as early as possible. It's better for little things like tail calls and other core PL concepts, there's this pile of very low level, obscure details. Rust also has this thing called the drop check flag. And so they actually, in the worst case dynamically track whether or not a slot on the stack is live or not.

I've known the Rust community for a long time and have a lot of respect for it. But also it's around 14 years old, roughly the same age as Swift. And we've learned a lot from that journey, Mojo represents is an opportunity to take the learning and do the next step, there are a bunch of ways to simplify it. Mojo also supports async await natively because that's obviously important for high performance threaded applications. We don't need pinning, which is a really big deal it turns out, because all values have identity. There are these very low level nerdy tweaks to the way the type system works, in Mojo you never get implicit memcpy because of moves.

### How does mutability and value semantics work in Mojo?

Summarized from 55:47 into the video

One of the things that we pushed and Swift has is functional programming, it made the observation that functional programmers say it's amazing because you never mutate data. You always get new values, and because you get new values, you get composition, you get predictability, you get control, you get all these different benefits of not having mutation. C programmers would flip that around and say that creating a new value every time you want to insert something into a list is very bad for performance, so nobody could ever build a real system on top of that. Swift says the thing that you want is exclusive ownership of a value to get value semantics which still allows local mutation. Rust has its own take on the same idea, if you have exclusive access to a value, you can mutate it. In Swift the Array, Dictionary, and String types are all immutable in the Java sense, where your know that the value will never change underneath you. And so it looks very much like a functional programming idiom. If I have a String, it can't change unless I change it. If I change it, it's not gonna break anybody else. And through the implementation, it never does deep copies implicitly. And so there's a bunch of stuff that was developed and works really well in the Swift ecosystem that I think will come over naturally into the Mojo ecosystem.

The goal is bring forward the wonderful things of functional programming. Composition locality of reference so you don't have the spooky action at a distance that reference based languages have. So this is all what I love about the functional programming model, but then we can also bring in-place mutations, so you also get the efficiency. Swift has some problems, it implicitly sometimes copies things a million times and, and so we've learned from that and fixed some of those problems in Mojo.

This is all opt in, if you want to use fully dynamic stuff that's totally fine, which is a system that can scale because we're not trying to change the existing world. What we're doing is filling in the missing world. One way to look at modern Python is that it's only half the language, underneath it is C. If you're building a large scale application in Python, you end up having C or Rust or something else that goes with Python. And so what we're doing is keeping the Python syntax, but then replacing the C and having one system that can do both. So instead of having to switch from Python, with C and FFI and bindings and all that nonsense, you can still have the __add__ like you're familiar with and everything just works.

### How does parallelization work in Mojo?

Summarized from 01:00:34 into the video

We push it into libraries, we have things like a parallel for loop, and they're just library functions. You can pass a nested function in, and so that's the easiest way. We have a very high performance, low level threading library. Today's systems are not just four or eight cores, they're servers with 256 cores, and it's only going to get more crazy in the next few years. This is the world that Mojo is designed for. We haven't built out an actor system for Mojo yet. Swift has a full actor system which is type safe, it's very good for large scale loosely coupled distributed agents, it even supports distributed actors. It builds right on top of async await in a very nice way, and so we may do that. Right now we're very focused on structured compute, more supercomputer style, and the numerics side of things, so we haven't prioritised an actor system yet. I would prefer it to be in the library if we can, to make sure that it's memory safe. Other threading libraries are not memory safe and that leads to certain challenges. There may be a benefit to putting some logic in the compiler to mediate accesses across actors, and then put the bulk of it in the library with type system support.

Most programmers just want to say, here's a parallel for loop, go nuts. From the systems level, you want to be able to support structured nested parallelism. You need thread libraries to compose, you need async await. So you're not getting bogged down with hundreds of thousands of threads that then kill your machine.

### How does CPU and GPU Composability work?

Summarized from 01:03:27 into the video

If you think about CPUs with 256 cores, GPUs have thousands of cores, or thousands of threads. And the programming model around a GPU is extremely different than the traditional CPU programming model. One of our goals is to make it so people can write much more portable algorithms and applications. It's easy to understand how you make a graph portable. You implement the graph for one type of hardware, and another implementation for another type of hardware. The power of being declarative is that you're separating out a lot of the implementation concerns. But then if you get down into writing for loops, they're imperative code at the bottom of the stack. And so we've carved out the ability for people to define their own abstractions in Mojo.

When you start talking about accelerators, really what ends up mattering a lot parallelism, but also memory. And so how you use the memory hierarchy is the most important thing these days, particularly for GPUs and LLMs in this world that we inhabit. Modern GPUs and CPUs have many level memory hierarchies. In a CPU is you've got a big vector register file which is your L0 cache, then you have an L1 cache, which is really fast and close to the CPU, an L2 cache which is sometimes shared with one or two cores, an L3 cache and it's shared with all of the cores, and main memory. The GPU has roughly the same idea, although the details are very different.

Inherent to getting high performance with something like matrix multiplication is not just doing a dot product, you have to process the workload and tiles. We've seen an emergence of various tile based programming models instead of writing afor loop doing a load, store, add, and multiply. Instead you're thinking about processing a tile at a time. What you do is you write the algorithm for a tile and then you use higher level orchestration logic that then says on this device, I'll traverse this way, or I will prefetch the data in two steps ahead, or I will get better reuse if I go vertically and some horizontally etc. There are all these tricks that the world has developed. In AI you get a generalization called a tensor, and so you take a two dimensional grid of numbers and you make it an n-dimensional grid of numbers. What really happens is it gets linearized in memory. But if you go down a row, you're jumping a whole rows worth of data which is a lot less efficient. When we get into multidimensional vectors, that becomes more pronounced.

In matrix multiplication typically you're going horizontally through the row of one matrix and you're going vertically through the row of another matrix to compute an output element. If you tell the GPU to arrange those two matrices differently, Â you can transpose it ahead of time, which makes things a lot more efficient. Instead of processing one row and one column at a time, you can process two rows and one column. What that means is when you're processing a column, you're accessing two elements next to each other, for example. And so as you generalize this you get the idea of a tile, a logical concept of processing a two dimensional block of memory composing against other things. Modern hardware not only is it complicated with vectors and threads etc. but they're now adding full on matrix operations to the silicon. You can literally do a matrix multiplication of a very small matrix, say 4x4 or 16x16 for some accelerators, up to 128x128. The intuition is that AI is important to the world and silicon is fundamentally two dimensional. If you use the two dimensional nature of silicon to put down a matrix multiplication, you can get a lot of performance and other benefits from that. A lot of the challenge is how do I use these accelerators, map tiles onto the devices, use the memory hierarchy efficiently, and this becomes as important as the numerics because the performance difference can be 10x or 100x.

We have the Fortran world, and various C template libraries. They were built to try and combat some of these problems. They came up with sometimes very powerful, but niche solutions and the usability was never very great. This is where you want to pull together Mojo's ability to talk to all these crazy hardware features, like the matrix multiplication operations, along with higher order combinators so that you can build libraries that can handle things like tiling. You don't want to know how the orchestration logic works, you only want to know how part of it works. The compile time metaprogramming then enables you to write really reusable and portable code. One of the things I recently gave a talk to at the NVIDIA GTC conference, is how this all composes together to make it so you can write high performance numerics for the same algorithm, which works on GPUs and CPUs.

If you're making a major investment in building software, you want it to last for 10 years or 20 years. You want to be able to adapt to the needs of new hardware. And hardware will continue to evolve, it's moving faster than ever. Mojo is helping break through some of these boundaries that have prevented people from building portable software, while still being able to utilize the high performance, super fancy features that people are coming out with. NVIDIA is a great citizen in the software world because every time they come out with a new chip, they provide LLVM access. We can talk directly into that stack, hardware makers are very LLVM friendly these days, and Mojo can talk to MLIR and LLVM and get direct access to all these hardware optimizations.

### Community and open source

Summarized from 01:17:04 into the video

Mojo is still a relatively young language which launched last May, so it's been public for less than a year. But it's doing really well, we have over 175,000 people that have used Mojo, we have a nice discord community that has over 22,000 people hanging out, and all the Mojicians like talking to each other and building cool stuff. Actually, as we record today, we're open sourcing a big chunk of Mojo. The entire standard library, which is the heart and soul of the language, is all open sourcing. We've been on a quest to open source more of the stack over time, so that's a really big deal. We've been public about this and telling people about it for a while, people have been waiting for it for a long time. We're seeing continued growth of the community and continued passion projects, and I think this will be a huge step.

Open source is very important to me, I built the LLVM community from scratch from my research project at university. I helped build the Swift open source community and worked in many other communities. What I've seen is that open source isn't just about having code on GitHub. Open source is about having an open community, having an inclusive way of developing code together, working together with a common goal. And so we put a lot of energy into not just providing source code, but also getting a contribution model, and picking the Apache 2 license and following best practices.

I'm really excited about that, I think that people are going to have a lot of fun and I look forward to being much more open with our development of Mojo. Please join our Discord, that's a great place to go. There's everything from folks that are interested in type theory nerdery, to AI researchers, to people that just want a better Python. And again, the cool thing about Mojo is that it's being built with state of the art to solve these hardcore problems at the frontier of computer architecture and programming languages. We're building in a way that It's completely general, although we're focused on AI as there's a lot of pain and suffering in that world. But it turns out that a lot of people write web servers and other things. And it's fantastic to see people building into that space, even though we personally don't have the expertise to invest in that.

The thing I love about Swift is I still get people that stop me in the street and say thank you for helping to drive this thing and make it happen. Because of you, I learned how to get into programming and Objective C was always too scary. These things take a couple of years to play out, but what I hope happens with Mojo is we get all these people that know Python and can continue to grow. Because they're not faced with this scary threshold of learning C or Rust. And if we can get more people involved, be more inclusive to good ideas, what I think we'll find is these technologies can go even further and have an even bigger impact.

- Download MAX and Mojo.

- Head over to the docs to read the Mojoð¥ manual and learn about APIs.

- Join our Discord community.

- Contribute to Â discussions on the Mojo GitHub.

- Read and subscribe to Modverse Newsletter.

- Read Mojo blog posts, watch developer videos and past live streams.

- Report feedback, including issues on our GitHub tracker.

Until next time! ð¥

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Engineering

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Chris Lattner

Co-Founder & CEO

Distinguished Leader who founded and scaled critical infrastructure including LLVM, Clang, MLIR, Cloud TPUs and the Swift programming language. Chris built AI and core systems at multiple world leading technology companies including Apple, Google, SiFive and Tesla.

clattner@modular.com

================================================================================

URL: https://www.modular.com/blog/whats-new-in-mojo-24-3-community-contributions-pythonic-collections-and-core-language-enhancements

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

May 2, 2024

# Whatâs New in Mojo 24.3: Community Contributions, Pythonic Collections and Core Language Enhancements

Shashank Prasanna

Mojoð¥ 24.3 is now available for download and this is a very special release. This is the first major release since Mojoð¥ standard library was open sourced and it is packed with the wholesome goodness of community contributions! The enthusiasm from the Mojo community to enhance the standard library has been truly remarkable. And on behalf of the entire Mojo team, weâd like to thank you for all your feedback, discussion and, contributions to Mojo, helping shape it into a stronger and more inclusive platform for all.Â

In addition to standard library enhancements, this release also includes several new core language features and enhancements to built-in types and collections that make them more Pythonic. Through the rest of the blog post, Iâll share many of the new features with code examples that you can copy/paste and follow along. You can also access all the code samples in this blog post in a Jupyter Notebook on GitHub. As always, the official changelog has an exhaustive list of new features, whatâs changed, whatâs removed, and whatâs fixed. And before we continue, donât forget to upgrade your Mojoð¥. Letâs dive into the new features.

### Enhancements to List, Dict, and Tuple

In Mojo 24.3 collections (List, Dict, Set, Tuple) are more Pythonic than ever and easier to use if youâre coming from Python. Many of these enhancements have come directly from the community:

- List has several new methods that mirror Python API, thanks to community contributions fromÂ  @LJ-9801 @mikowals @whym1here @StandinKP.

- Dict can now be updated thanks to contributions from @gabrieldemarmiesse.

- Tuple now works with memory-only element types like String and allows you to directly index into it with a parameter expression.

One of the best ways to learn new features is to see them in action. Letâs take a look at an example that makes use of these types. In the example below we implement a simple gradient descent algorithm, which is an iterative algorithm used to find the minima of a function. Gradient descent is also used in most machine learning algorithms to minimize the training loss function by updating the values of function parameters (i.e. weights) iteratively until some convergence criterion is met.

For this example, we choose the famous Rosenbrock function to optimize, i.e. find its minima. Rosenbrock is an important function in optimization because it represents a challenging landscape with a global minimum at ((1,1) for 2-D Rosenbrock) that is difficult to find. Letâs take a look at its implementation and how we make use of Mojoâs shiny new List, Dict, and Tuple enhancements. First, we define the Rosenbrock function and its gradient:

We use Tuple to return the gradients using (dx, dy). Notice that for the return type we use Tuple[Float64, Float64], we can also write it more simply as (Float64, Float64) using parentheses just like in Python. Now we can write the gradient descent iteration loop and we'll use the parentheses style for Tuple. This simplifies compare List[Tuple[Float64, Float64, Float64]]() vs List[(Float64, Float64, Float64)]() below:

Here we use List to store gradients and function evaluation at each iteration using history.append((x, y, rosenbrock(x, y)))

We also capture the Tuple output of rosenbrock_gradient in grad. You can index into grad to access dx = grad[0] and dy = grad[1] which we use to update x and y.Â

Finally, we call the gradient_descent function with a dictionary of parameters params:

Output

Note: plot_results() is a Python function that I call from Mojo using Mojo-Python interop. Since we capture all the iterations of (x,y) in our List[Tuple] variable history we have all the information we need to generate these plots below. We do, however need to covert history into a NumPy array before we call our Python function, which is what we do in the for loop at the end. You can find the implementation of this function on GitHub along with rest of the code.

In the plot above (click to zoom) you can see that we start at an initial point (0,3) and gradient descent takes us to the global minima at (1,1)

We can use the new update() function in Dict to update using params.update(new_params) to change our initial point to (-1.5,3) and re-run the optimization:

Output

With the new initial point you can see in the contour plot (click to zoom), that due to the narrowness of the valley, the gradient descent algorithm overshoots the minimum and bounces back and forth across the valley, causing oscillations. Such problems are common in numerical optimization problems and can lead to slow or premature convergence. This tells us that we can explore different learning parameters (or hyperparameters in machine learning) or other types of optimizers to converge faster.

### Enhancements to Set

Sets are unordered collections of unique elements, allowing for efficient membership tests and mathematical set operations. An example of using Sets can be to identify unique genetic markers or species from a large dataset of DNA sequences. Sets can automatically handle duplicates, and offer efficient operations for mathematical set concepts like unions, intersections, and differences.

In this release, Set introduces named methods that mirror operators, thanks to contributions from @arvindavoudi

Letâs take a look at a simple example that compares both operator based and the new method based operations on the set. Letâs define two sets with different genetic markers:

We can use both difference method and difference operator to subtract both sets:

Output

Similarly, we can perform intersection_update using the method and the operator &=Â :

Output

Finally, we can use the new update method to update a set:

Output

### New reversed() function for reversed iterator

This release includes a new reversed() function for reversed iterators thanks to community contribution from @helehex @jayzhan211. In this example below, we reverse the words in a sentence using the new reversed iterator and by using Listâs reverse() method and compare their results:

Output

reversed() function for reversed iterator also supports Dicts.

### New parametric indices in __getitem__() and __setitem__(), and Dict, List, and Set conform to the new Boolable trait.

Mojo 24.3 also includes new core language enhancements and introduces a new Boolable trait. In the example below weâll explore both these features. Weâll create a struct called MyStaticArray whose size is known at compile time. For the MyStaticArray struct we can choose to define parametric indices __getitem__[idx: Int](self) and use compile-time checks on the requested index. This is in contrast to using __getitem__(self, idx: Int) which can be used when the size of the array is unknown at compile time. Letâs take a closer look at the struct:

Letâs instantiate the MyStaticArray and since it conforms to Boolable trait, we can check if the array is empty. We can use Bool(arr) or just arr in the if condition, we show both approaches below:

Output

Now letâs try to get an item from the MyStaticArray whose index is larger than the size of the array.

Output

This fails the constrained[idx<size, â¦]() test in __getitem__[]() function.

### But wait, there is so much more!

Mojo 24.3 is a huge release and I barely scratched the surface in this blog post. While this blog post was focused on community contributions, standard library enhancements, and a few core language enhancements, there is a lot more in this release that also caters to low-level system programming. I encourage you to check out the detailed list of whatâs new, changed, moved, renamed, and fixed, check out the changelog in the documentation. A few other notable features from the changelog:

- Core Language: Improvements to variadic arguments support.

- Core language: Allows users to capture the source location of code and call the location of functions dynamically using the __source_location() and __call_location() functions.

- Standard Library: FileHandle.seek() now has a "whence" argument similar to Python.

- Docs: New Types page.

MAX 24.3 is also available for download today and includes several enhancements including preview of Custom Operator Extensibility support which allows you to write custom operators for MAX models using the Mojo for intuitive and performant extensibility. Read more in the Whatâs new in MAX 24.3 blog post.

All the examples I used in this blog post are available in a Jupyter Notebook on GitHub, check it out!

- Download MAX and Mojo.

- Head over to the docs to read the Mojoð¥ manual and learn about APIs.

- Explore the examples on GitHub.

- Join our Discord community.

- Contribute toÂ  discussions on the Mojo GitHub.

- Read and subscribe to Modverse Newsletter.

- Read Mojo blog posts, watch developer videos and past live streams.

- Report feedback, including issues on our GitHub tracker.

Until next time! ð¥

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Developer

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Shashank Prasanna

AI Developer Advocate

Shashank is an engineer, educator and doodler. He writes and talks about machine learning, specialized machine learning hardware (AI Accelerators) and AI Infrastructure in the cloud. He previously worked at Meta, AWS, NVIDIA, MathWorks (MATLAB) and Oracle in developer relations and marketing, product management, and software development roles and hold an M.S. in electrical engineering.



================================================================================

URL: https://www.modular.com/blog/multimodal-search-with-snowflake-embedding-and-max-engine

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

April 26, 2024

# Multimodal Search with Snowflake Embedding and MAX Engine

Ehsan M. Kermani

In our previous blog post onÂ  semantic search with MAX Engine, we demonstrated that MAX Engine significantly outperformed both PyTorch eager and ONNX runtimeâby factors of 2 to 2.8 times, respectively, across different batch sizes. Today, we're exploring how a multimodal approach can further enhance semantic search by combining textual and visual data and we will discuss how MAX Engine can optimize multiple models for inference.

The integration of multiple data types in semantic search presents a unique challenge: how to effectively blend and understand information that comes in different forms, such as text and images. This approach is crucial because it mirrors the way humans process information, leveraging both visual cues and textual context. Pioneering models, such as OpenAI CLIP, have successfully merged text and image understanding through a single neural network trained on vast amounts of data.

For this blog, we will use lightweight models such as snowflake-arctic-embed-m which is a state-of-the-art, pretrained text embedding model and MobileNetV2 image classification model that we extract the logits for our image embeddings. Inspired by CLIP, we create a custom deep learning model that takes image and text embeddings and maps them into a common shared space. For training, we utilize a contrastive loss function, which helps in aligning the text and image embeddings in the shared latent space. Additionally, we use the Flickr30k dataset which contains 31,000 images collected from Flickr, together with 5 reference sentences (captions) provided by human annotator, for training, evaluating and testing our multimodal model.

We will demonstrate how to convert the models to TorchScript and optimize them in MAX Engine for inference. Lastly, we compute the cosine similarity matrix of the image-caption pairs in the test dataset and conclude by visualizing the ground-truth captions alongside the top five most similar captions found using the similarity matrix.

To install MAX, please check out Get started with MAX Engine. Also have a look at Getting Started with MAX Developer Edition in case you missed it.Â

The code for this blog post is available in our GitHub repository. The MAX version for this blog is max 24.2.1 (58157dc0).

### Data inspection

We start by downloading the Flickr30k dataset which is a comprehensive collection of 31,000 images sourced from Flickr, each accompanied by five captions written by human annotators.The dataset is publicly available on HuggingFace. To construct our (image, caption) pairs, we simplify the dataset by randomly selecting one out of the five available captions for each image, ensuring that each pair consists of one image and its corresponding caption.

The dataset is divided into three distinct parts, which serve different roles throughout our experiments:

- Train: This subset is used for training our model, allowing it to learn and adapt to the task of associating images with textual descriptions.Â

- Validation: This subset helps in tuning the model and validating its performance during training, providing a way to monitor overfitting and make adjustments to model hyper-parameters.Â

- Test: Used for evaluating the modelâs performance after training, this subset helps assess how well the model generalizes to new, unseen data.

For effective visualization and interaction with the dataset, we use the fiftyone Python package that is particularly suited for tasks like ours because it offers a variety of tools that facilitate the quick construction, visualization, and analysis of image datasets.Â

Here is our the dashboard looks like

Note that when using the fiftyone package over SSH, by default the server runs on port 5151. To access it remotely, we need to setup port forwarding via ssh -L 5151:localhost:5151 username@server.

### Building our own multimodal model

Our multimodal model consists of three parts, each of which we will explain in detail.

#### Text embedding

For text embedding, we use snowflake-arctic-embed-m which is a state-of-the-art, pretrained text embedding model with 768 embedding dimensions that supports up to 512 tokens and occupies 436MB of disk size. It's particularly suitable for CPU-only applications due to its low runtime memory usage of 0.41GB (float32), which optimizes performance without compromising speed.Â

Below is the code snippet demonstrating how to load the model, compute the query embeddings, and normalize them for subsequent processing.

This function will be integral to our semantic search system, allowing us to accurately and efficiently process textual information alongside visual data, thereby enhancing the overall search capability.

#### Image embedding

For the image component of our multimodal search system, we use the MobileNetV2, a lightweight classification model pretrained on the ImageNet-1k dataset. This model is designed to handle images of 224 by 224 resolution, making it an excellent choice for efficient image processing without sacrificing accuracy.Â

Here's how to load the model and compute the normalized logits, which we use as embedding values. This process ensures that the embeddings are on a consistent scale, which is crucial for accurately comparing images in our search system.

This method provides a streamlined approach to obtaining image embeddings, which we will use in the next section.

#### Custom multimodal model

The following is an example of a simple multimodal model, designed to integrate text and image embeddings effectively. This model aims to project both types of embeddings into a common space where their similarities can be compared directly, which is crucial for tasks like multimodal semantic search.Â

Here's a breakdown of how the model is structured and functions:

The model consists of

- Text and Image Transformation Modules: These are simple neural networks consisting of a linear layer followed by a ReLU activation function. The linear layer reduces the dimensionality of the text and image embeddings to common_dim, making the embeddings more manageable and focused on the most essential features for comparison.Â

- Final Linear Transformation: This is a shared linear layer applied to both the transformed text and image embeddings. By projecting both types of embeddings into the same space with the same transformation, we ensure that they are directly comparable. This is crucial for tasks that involve finding the similarity between text and images.Â

Overall, this simple model facilitates the interaction between text and image data in a meaningful way, which enhances the capability of our multimodal semantic search. By ensuring that text and images are represented in the same vector space, we improve the system's ability to match text queries with relevant images and vice versa.

#### Contrastive loss function

For our model to be able to distinguish between similar and dissimilar pairs of data, we use a contrastive loss function. The core idea behind contrastive loss is to ensure that representations of similar items are pulled closer together in the embedding space, while representations of dissimilar items are pushed apart. This is typically achieved by comparing the distance between embeddings with a marginâa threshold beyond which the distance between dissimilar pairs should ideally lie.Â

One foundational paper that discusses the use of contrastive loss in deep learning is "Dimensionality Reduction by Learning an Invariant Mapping" by Hadsell, Chopra, and LeCun (2006) which has since been widely adopted and adapted for various applications in machine learning, particularly in supervised and semi-supervised settings where relational knowledge between data points is crucial.

Our ContrastiveLoss class is a specialized module for calculating contrastive loss, which is particularly effective for learning tasks where the goal is to learn similarities and differences between pairs of itemsâin our case, between image and text embeddings. We will use a simpler form (without including negative pairs i.e. negative hard mining) of the following formulation from SimCLR paper, where sim is the cosine similarity function, tau is the temperature.

Here's a breakdown of its components and functionality:

- Temperature: This scalar is used to scale the cosine similarity outputs, which affects the sharpness of the distribution of output probabilities. A lower temperature makes the distribution harder, amplifying the differences between the more and less similar pairs, which is crucial for training stable and robust models.Â

- Cosine Similarity: This function measures the cosine of the angle between two vectors. In this model, it is used to calculate the similarity between the embeddings of images and texts. By treating the embeddings as vectors in a high-dimensional space, the cosine similarity directly correlates to how similar the actual content of the texts and images are.Â

- Logits Calculation: The cosine similarities are computed between each image and text embedding pair, scaled by the temperature, and structured in a matrix where each row corresponds to an image and each column to a text. This matrix setup facilitates the comparison of each image with each text.Â â

- Labels and Loss Calculation: The true matches between images and texts are on the main diagonal of the logits matrix (since each row and column index of the diagonal corresponds to paired image and text embeddings). The CrossEntropyLoss function then uses these labels to compute the loss, which encourages the model to correctly align the embeddings of corresponding images and texts.

#### Train and evaluation

To initiate training of our custom multimodal model, we will keep the setup straightforward by running the training loop for only five epochs without adjusting any hyper-parameters. This approach allows us to quickly validate our model's architecture and initial performance. Here is the Python code that handles the training process and saves a checkpoint at the end of each epoch to track the model's progress and potentially resume training if needed.

Once the training is complete, we can visualize the training and validation losses. For the remainder of this blog post, we will use the checkpoint from epoch 2.

### Inference with MAX Engine

In this section, we will see how to prepare the models to be used with the MAX Engine.

#### Convert to TorchScript

MAX Engine requires PyTorch models to be in TorchScript formats. Below, we will demonstrate how to convert all three parts of our modelâthe image embedding, text embedding, and the custom multimodal partâinto TorchScript format.

Note that for PyTorch tracing, we only need to provide dummy inputs but with correct shapes as follows

We are now ready to employ MAX Engine to optimize our models for fast inference.

#### Load and optimize models with MAX Engine

Next, we create a session object and provide input specifications for all three parts, loading them into their optimized MAX models. Here, we use MAX Engine dynamic shape capabilities to compile the models. For example, to compile the models for dynamic batch sizes, we use None as the first dimension of TorchInputSpec as follows

and for the text embedding to account for variable sequence lengths ,we use None as the second dimension in TorchInputSpec

#### Create similarity matrix and inspect the results

In this section, we use our optimized MAX models to generate embeddings for images and captions from our test dataset and then compute the projected embeddings into a common space. This setup allows us to create a similarity matrix, enabling us to assess and visualize the alignment and correlation between image and text embeddings. For demonstration we use a fixed batch size of 16, but note that our model was compiled to support any feasible batch sizes. In real world use-cases if the batch size is fixed for inference, MAXÂ Engine can optimize the model further. For that we no longer need to use None in TorchInputSpec and we can use a predetermined fixed batch size value in the compilation process.

None

Given the cosine_similarities which is a matrix of shape 1000 by 1000 (since there are 1000 images in the test dataset), we can retrieve the k highest values and their corresponding indices from the cosine similarity matrix. In this case, k is set to 5, meaning the code identifies the top 5 most similar text embeddings for each image embedding. This is useful for tasks like retrieving the most relevant textual descriptions for given images or vice versa.

Finally, we can visualize the top 5 captions for each image using the following approach. This visualization not only allows us to review the most relevant captions identified by our model but also serves as a critical assessment tool. As an example, is theÂ

- ground-truth caption: A man is an orange hat starring at something

- top predicated caption: A man with glasses is wearing a beer can crocheted hat

Additionally, here is a view of the test dataset with both ground-truth and predicted captions. This side-by-side comparison provides a clear illustration of our modelâs performance, allowing us to directly assess the accuracy of the predictions against the actual captions.

### Next steps

Here are a few potential next steps to consider

- Experiment with different image and text embeddings to assess the impact on your results.

- Modify the architecture of our custom model and perform hyper-parameter tuning.

- Use evaluation metrics such as Precision@k and NDCG@k to obtain more reliable numerical results.

- We are excited to see what you build! ð Share your own end-to-end MAX pipelines with us.

### Conclusion

In this blog post, we have explored the advantages of employing a multimodal approach to semantic search using the MAX Engine. We demonstrated how to create and train a multimodal model for enhanced search capabilities, and we showed how to effectively use MAX Engine when dealing with multiple interconnected models. By visualizing the top predicted captions alongside the ground-truth data, we were able to conduct a direct assessment of our modelâs applicability. This not only highlighted the potential of multimodal systems in understanding complex datasets but also underscored the importance of continuous refinement and testing to achieve high accuracy and relevance in search results.

Additional resources:

- Get started with downloading MAX

- Download and run MAX examples on GitHubÂ

- Head over to the MAX docs to learn more about MAX Engine APIs and Mojo programming manualÂ

- Join our Discord communityÂ

- Contribute to discussions on the Mojo and MAX GitHub

Report feedback, including issues on our Mojo and MAX GitHub tracker.

Until next time!ð¥

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Developer

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Ehsan M. Kermani

AI DevRel

Ehsan is a Seasoned Machine Learning Engineer with a decade of experience and a rich background in Mathematics and Computer Science. His expertise lies in the development of cutting-edge Machine Learning and Deep Learning systems ranging from Natural Language Processing, Computer Vision, Generative AI and LLMs, Time Series Forecasting and Anomaly Detection while ensuring proper MLOps practices are in-place. Beyond his technical skills, he is very passionate about demystifying complex concepts by creating high-quality and engaging content. His goal is to empower and inspire the developer community through clear, accessible communication and innovative problem-solving. Ehsan lives in Vancouver, Canada.



================================================================================

URL: https://www.modular.com/blog/preventing-supply-chain-attacks-at-modular

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

April 25, 2024

# Preventing Supply Chain Attacks at Modular

Zac Bowling

Swetha Muniraju

At Modular, the pace of innovation means we are always shipping new software and features for our users. While this is really exciting, it also presents some unique challenges. Modern software and delivery mechanisms are more vulnerable to security attacks than ever before. It is estimated that in 2024, 96% of total codebases contained open-source code with an average of 500+ components per app. Recent events, such as the sophisticated XZ supply chain attack, have highlighted the urgent need for robust defenses against these types of vulnerabilities in the supply chain. At Modular, a secure software delivery strategy has been our core tenet since we shipped our first version of Mojo back in the Fall of 2023.

### The ecosystem today

Traditionally, software delivery systems have relied on a combination of SSL/TLS for downloading software from a trusted source, signing systems like GPG, and verified cryptographic hashes to validate that packages were not tampered with. You can see these techniques used in package managers like PyPI, apt, YUM, and others. However, only coarse-grained approaches to security are used in these systems and they are still vulnerable to attacks as they always assume the source repository can be trusted. In addition, none of these methods guard against the modern supply chain attacks as they can happen from any point from package creation to deployment. Nor are they designed to help recover when such attacks occur. For example, say your signing keys are compromised, many of these package management systems are ill-equipped to quickly handle the aftermath without manual user intervention.

Given all of these constraints, we at Modular had a set of questions we wanted to solve.

- How do we deal with a potential leak of our signing keys?

- How can we revoke software we have shipped that have known vulnerabilities introduced either by third-party dependencies or by us?

- How can we help the user validate that the installed software is genuine and not tampered with in the distribution supply chain, especially when it's delivered by our CDN partners, who may have been compromised?

### Building on âThe Update Frameworkâ standard

The Update Framework (TUF) specification was written to overcome some of the security vulnerabilities faced by using traditional package managers. TUF publishes standards and techniques to update software securely and to handle supply chain attacks. It validates files using public/private shared key encryption and provides a mechanism for quickly rotating keys without necessarily invaliding all the previous releases of the software. In addition to handling key compromises swiftly, TUF also prevents malicious mirror update attacks, replay attacks, and a host of other attack vectors. A full list of attacks that TUF protects against are listed in detail here.

The TUF organizers provide a few reference implementations to get started with; however, these are not out-of-the-box general-purpose software update solutions. This is because different software deployment systems and their accompanying packages may have complex needs depending on the environments they run. For that reason, TUF doesnât specify concepts like versioning, installation requirements, system compatibility, or even the actual method of software delivery (HTTPS, BitTorrent, etc.). To complete that story for Modular, we had to build our client implementation of TUF with all the additional features that you might find in a package manager.

### The modular CLI

When considering all these problems with secure software packaging and distribution we found there wasn't one out-of-the-box cross-platform solution that we could simply adopt that solved all of these problems for us. This was one of the many reasons that led us to build the modular CLI (https://docs.modular.com/cli/).

If you have already installed the MAX and/or the Mojo standalone package, chances are that you have already interacted with our modular CLI. The modular CLI is how we securely distribute the Mojo, Mojo nightly, and MAX packages. The modular CLI is installed as a signed system native package, which provides the first layer of security. For this layer, we are still constrained by some of the challenges that we had outlined earlier. However, we mitigate some of these risks via an authentication in the next step.

### First round of security - authentication

After installing the modular CLI, we ask users to authenticate the modular CLI to get started.

Authentication provides yet another opportunity to validate the modular CLI tool and prevent the user from continuing with untrusted packaging. It provides some security against replay attacks where an intruder works to keep a user on an older and possibly compromised version of the client that could be used to try and install other compromised software.

### Second round of security - package verification

After authentication, users can use the modular CLI to install a number of packages. Today this includes the full MAX, Mojo standalone, or Mojo nightly packages.

â

The modular CLI does several things, including checking for the latest version of the package, finding a compatible and optimized version for your system, downloading the package, and securely validating it before attempting to install it.

### What does this mean for you?

The best part about this is that you shouldn't have to worry about any of this. Good security is hard work, but it is at its best when you don't have to think about it. We are continuing to drive state of the art techniques into all levels of our technology and products, and will continue to ensure you get the right bits delivered securely

### What's next?

As we grow our community, we are excited to scale our modular client to support different platforms. We'll also continue to introduce delightful new features so you can continue to fall in love with our products and technologies. We are constantly listening to your feedback and adapting our roadmaps to cater towards our community needs. If you have any comments or feedback, please reach out to us on discord channels or via GH-Issues.

If you are interested in contributing to the work that we are doing at Modular, thereâs good news for you - We are hiring! Please visit https://www.modular.com/careers#open-roles and apply to the open positions.

â

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Engineering

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Zac Bowling

AI Framework Engineer

Zac is a seasoned software engineer with over 20 years of experience. He has a specialty in developing embedded and mobile operating systems and building developer tooling and SDKs for platforms like Apportable and Google. Prior to joining Modular, Zac worked at Google for nearly 8 years, where was an early developer bringing up the new Fuchsia operating system.



Swetha Muniraju

AI Frameworks Manager

Engineering leader with experience managing large scale distributed backend, and applied machine learning services in production. Prior to joining Modular, Swetha served as a media tech backend engineering leader at Twitter. During her time at Twitter, she was overseeing the global team responsible for video, and image stack delivering live streaming, VOD and Audio Spaces products. She has also led global engineering and research teams at Nokia serving as head of perception and localization by building zero to one products in the field of applied machine learning. Swetha loves spending time outdoors with her family skiing or hiking depending on the season.



================================================================================

URL: https://www.modular.com/blog-all?topic=Culture

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/team/tim-davis

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?author=Chris+Lattner

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?author=Tim+Davis

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/a5f21428f399453b9206416be52aa141

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog/a-unified-extensible-platform-to-superpower-your-ai

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

May 2, 2023

# A unified, extensible platform to superpower your AI

Chris Lattner

Tim Davis

Eric Johnson

## Improving AI for the world

If youâve followed Modular since our founding in early 2022, you know weâve been talking a lot about how AI software is broken, fragmented, too expensive, and negatively impacts AI developers and the world. Weâve written a manifesto about why the world needs better AI software, discussed the importance of modularity in AI software, explained how current tech stacks struggle with large models, and described where todayâs AI serving stacks fall short. Â We only recently started sharing our results, including that we developed the worldâs fastest matrix multiplication algorithms across multiple CPU targets.

This post is different. Weâre excited to finally share what weâve been building at Modular. This announcement begins Modularâs journey to radically change the nature of AI programmability, usability, scalability, and compute.

We strongly believe in a world that gives fair and equitable access to AI for all. To achieve our vision of enabling AI to be used by anyone, anywhere, we are rethinking and reinventing the entire AI stack from the ground up. Our next-generation developer platform scales to help you defragment your AI and compute stacks and realize more value from the incredible pace at which AI is evolving.

## Two incredible breakthroughs

Modular is moving AI infrastructure from the research era into the production era. AI infrastructure itself has been rapidly evolving under heavy research for years now. Still, the lessons learned needed to be brought forward into a production-quality system that combines first-principles thinking with rigorous engineering and a design that scales to help unify the world. To that end, we are excited to announce two new technological breakthroughs on our next-generation AI developer platform:

- The worldâs fastest unified inference engine that is easy to use, portable, and highly performant, making it easier than ever to power your AI models in production, and save money in the process.

- Mojoð¥, a programming language for all AI developers that combines the usability of Python and the performance of C, bringing programmability back to AI while unifying the hardware landscape.

Letâs dive deeper into each.

### The worldâs fastest unified inference engine

We built the Modular Inference Engine to defragment and simplify AI deployment. It is the worldâs fastest unified AI execution engine, powering all your PyTorch and TensorFlow workloads while delivering significant usability, performance, and portability gains.

â

With the Modular Inference Engine, the benefits to AI developers are clear:

- Deploy more models, faster: Powered by state-of-the-art technologies, the Modular Inference Engine allows developers to get rid of bespoke toolchains, radically simplifying AI deployment. Through simple APIs available in popular languages like Python and C/C++, developers can quickly deploy models trained using PyTorch and TensorFlow without intermediate model conversions or pre-optimization steps.

- Full compatibility with existing frameworks and servers: Unlike other AI engines, the Modular Inference Engine natively supports all the operators available in the latest versions of the training frameworks. It is also fully compatible with models containing control flow and fully supports dynamic shapes (such as text sequences of varying lengths that power models such as BERT and GPT). It is extensible by design, allowing developers to write their own custom operators. It is also fully compatible with existing cloud serving solutions such as NVIDIAâs Triton Inference Server and TensorFlow Serving, and supports on-prem deployment as well.

- Take advantage of the latest hardware: The Modular Inference Engine puts portability in the hands of every developer. The same installation packages âjust workâ everywhere regardless of the platform, micro-architecture capabilities (AVX512, AVX2, Neon), hardware type (CPU, GPU, xPU), or vendor. This means in practice that developers can now migrate workflows to new targets without changing toolchains or rewriting code. The ability to quickly experiment with different hardware allows businesses to gather the data required to make well-informed cost/performance tradeoffs - instead of guessing.

- Maximize performance, minimize costs: Unlike other solutions in the market, developers do not have to trade performance to unlock portability. The Modular Inference Engine is incredibly fast, delivering 3-4x latency and throughput gains out-of-the-box on state-of-the-art models across Intel, AMD and Graviton architecturesâunlocking compute everywhere. Check out performance.modular.com for a detailed breakdown of latency, throughput, and total inference cost across popular AI architectures.

The Inference Engine is available now to a limited number of early-access partners, and developers can join the waitlist to get access here. You can also check out a preview of ourÂ Python and C/C++ APIs.

### Mojo ð¥: A programming language for all AI developers

Thatâs right, we built a new programming language: we had to to defragment AI software development.

Mojo ð¥ combines the usability of Python with the performance of C, and adds new abilities to scale to AI accelerators. This unlocks unparalleled programmability of AI hardware and extensibility of AI models, without breaking what you love about Python.

Python is a powerful high-level language, complete with clean and easy to learn syntax and an expansive ecosystem of libraries: Python powers almost all AI research today. However, it also has well-known scalability issuesâit doesnât get you the worldâs largest workloads nor does it get you to edge devices. Instead, production AI deployment happens in other languages like C++ and CUDA. The result is a fragmented AI software landscape that reduces AI developer productivity and slows the pipeline from research to production.

Mojo solves these problems, bringing superpowers to AI developers:

- Write everything in one language: Mojo combines the parts of Python that researchers love with the systems programming features that require the use of C, C++ and CUDA. Mojo is built on top of next-generation compiler technologies that unlock significant performance gains when you add types to your programs, enables you to define zero-cost abstractions, benefit from Rust-like memory safety, and that powers unique autotuning and compile-time metaprogramming capabilities.

â

- Unlock Python performance: Mojo is built on Modularâs high performance heterogenous runtime and uses MLIR, which enables it to access all AI hardware. This gives Mojo access to threading, low-level hardware features like TensorCores and AMX extensions, and reach into accelerators. Â And oh yeah itâs fastâ¦ very fast! In fact, Mojo is 35,000x faster than Python when running numeric algorithms like Mandelbrot because it can take full advantage of your hardware.

â

- Access the entire Python ecosystem: But all this power doesnât mean you have to sacrifice what you already know and love. Mojo doesnât just look and feel like Python - it also gives you access to the full Python ecosystem, including favorites like Numpy, Pandas, Matplotlib, and also your other existing custom Python code.

- Upgrade your models and the Modular stack: Mojo isnât a side project â all of Modularâs Â in-house kernels are written in Mojo, which is why the Modular Inference Engine has such amazing performance and portability. The engine lets you extend your models with Mojo pre and post-processing operations, and replace existing operations with custom ones. Mojo enables you to customize the Modular stack with kernel fusion, graph rewrites, shape functions, all without having to recompile the framework or write any C++ or CUDA.

Mojo ð¥ is still in development but itâs available to try today with our JupyterHub-based Mojo Playground. Please visit Modular.com/mojo to sign up and gain access.

## The start of an incredible journey

Modular is striving to radically reshape the nature of AI and accelerated compute. Today is just the very beginning a long journey. Â We have a wide vision and want to enable AI to be used by anyone, anywhere. We hope that our infrastructure acts as a catalyst to unlock more AI developers, more innovation, and ultimately better AI products.

Join the revolution â watch our launch keynote to learn more, chat with us on our Discord and visit Modular.com to try us out in early preview. Letâs change the world together!

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Product

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Chris Lattner

Co-Founder & CEO

Distinguished Leader who founded and scaled critical infrastructure including LLVM, Clang, MLIR, Cloud TPUs and the Swift programming language. Chris built AI and core systems at multiple world leading technology companies including Apple, Google, SiFive and Tesla.

clattner@modular.com

Tim Davis

Co-Founder & President

Repeat Entrepreneur and Product Leader. Tim helped build, found and scale large parts of Google's AI infrastructure at Google Brain and Core Systems from APIs (TensorFlow), Compilers (XLA & MLIR) and runtimes for server (CPU/GPU/TPU) and TF Lite (Mobile/Micro/Web), Android ML & NNAPI, large model infrastructure & OSS for billions of users and devices. Loves running, building and scaling products to help people, and the world.

tim@modular.com

Eric Johnson

Product Lead

Product leader who has built and scaled AI applications and infrastructure. Eric led the TensorFlow API, Compiler, and Runtime teams at Google Brain and Core Systems, including the founding of TFRT and the productionization of JAX. He holds an MBA from Wharton and Computer Science MS from Penn and loves soccer, fitness, and the great outdoors.

eric@modular.com

================================================================================

URL: https://www.modular.com/blog/an-easy-introduction-to-mojo-for-python-programmers

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

August 8, 2023

# An easy introduction to Mojoð¥ for Python programmers

Shashank Prasanna

Learning a new programming language is hard. You have to learn new syntax, keywords, and best practices, all of which can be frustrating when youâre just starting. In this blog post, I want to share a gentle introduction to Mojo from a Python programmerâs perspective. Rather than focus on the language details such as Mojoâs programming model and syntax, which you can find in the Mojo programming manual, Iâll focus on an example-driven introduction that will gently guide you to a land of Mojo familiarity. The example used in this blog post is available in the Mojo Playground, so if you havenât already signed up for Mojo - do so now!

### Mojoð¥: A familiar approach

Mojo should feel very familiar to any Python programmer, as it shares Pythonâs syntax. But there are a few important differences that youâll see as we port a simple Python program to Mojo. The first thing youâll notice is that Mojo really shines in the performance department. âBut Python is no slouch â NumPy is really fast!â you might say, and youâd be right. However, if you look under the hood of NumPyâs elegant Python API, youâll see that all the computationally intensive code is written in C/C++, which is where its performance comes from.

With Mojo, you can write high-level code like Python and leverage Mojoâs lower-level features to explicitly manage memory, add types, etc., to get the performance of C (or better!). This means you get the best of both worlds in Mojo and donât have to write your algorithms in multiple languages.

Before we get started, here are a couple of housekeeping items:

- Mojo is still very early in its development phase and the language, and tooling arenât ready to support the migration of large Python projects. We expect that Python users will initially port small, computationally demanding sections of their code to Mojo and then migrate more significant parts of their code base over time as the language and tooling mature. We are adding many new language features each week, and you should follow the regular Changelog to get updates.

- The Mojo Playground environment is not always stable. You will be able to reproduce the output results of the calculations but the performance (execution time) may vary, and you may not see the performance shown below. The goal of this blog post is to introduce you to Mojo, not to benchmark its performance.

### From Python to Mojo: A simple example

Letâs start with a simple example that calculates the Euclidean distance between two vectors. This is mathematically expressed as the L2-norm of the difference vector $||\vec{a}-\vec{b}||$ where $\vec{a}$ and $\vec{b}$ are two n-dimensional vectors, and Iâll discuss the implementation details in the algorithm section below. Euclidean distance calculation is one of the most fundamental computations in scientific computing and machine learning, used in algorithms like k-nearest neighbors and similarity search. In this example, youâll see how you can get faster-than-NumPy performance on this task, using high-dimensional vectors with Mojo. Itâs a computationally intensive problem, so weâll build a solution from scratch, starting with Python, and bring it over to Mojo to improve performance.Â

My goal with this example is not to build the fastest program for this task, but to introduce Mojo and its syntax as a Python programmer.Â

#### Where do I run this example? - The Mojo Playground

The code in this blog post can be copied and pasted into a new Jupyter notebook on Mojo Playground. First, head over to playground.modular.com to access or sign up to access Mojo on a hosted JupyterLab server. Once you have your playground open, create a new Notebook. Paste each code block in this blog post into a new Jupyter Notebook cell and press the Run button on the menu bar or hit Shift+Enter on your keyboard to run the cell and see the output. We're working to make the code in this blog post available as a Notebook in Mojo Playground soon!

â

#### Algorithm implementation details

Calculating the Euclidean distance is fairly straightforward:Â

- Calculate the element-wise difference between two vectors to create a difference vector

- Square each element in the difference vector

- Sum up all the squared elements of the difference vector

- Take the square root of the sum

These 4 steps are illustrated in the diagram below:

â

In our implementation, the dimension of the vector n is the number of elements in our array or list. In pure Python, youâd write it down like this:

def python_naive_dist(a,b):
    s = 0.0
    n = len(a)
    for i in range(n):
        dist = a[i] - b[i]
        s += dist*dist
    return sqrt(s)

#### Euclidean distance in pure Python

First, letâs set a baseline by running and benchmarking pure Python performance for the Euclidean distance calculation. To verify the distance calculation is numerically accurate across Python and Mojo implementations, weâll create two random NumPy arrays of 10 million elements each and re-use them throughout the example. For the pure Python implementation, weâll convert these NumPy arrays into Python lists, so we only use data structure native to Python.

Mojo Playground tip: Add the %%python at the top of the Jupyter to instruct the Mojo Jupyter kernel to run this code as Python interpreted code and not as Mojo compiled code.Â

First, letâs create 2 random vectors with 10,000,000 elements using the code below.

%%python
import time
import numpy as np
from math import sqrt
from timeit import timeit

n = 10000000
anp = np.random.rand(n)
bnp = np.random.rand(n)

alist = anp.tolist()
blist = bnp.tolist()

def print_formatter(string, value):
    print(f"{string}: {value:5.5f}")

â

Now, weâre ready to calculate the Euclidean distance in pure Python.

%%python
# Pure Python iterative implementation
def python_naive_dist(a,b):
    s = 0.0
    n = len(a)
    for i in range(n):
        dist = a[i] - b[i]
        s += dist*dist
    return sqrt(s)

secs = timeit(lambda: python_naive_dist(alist,blist), number=5)/5
print("=== Pure Python Performance ===")
print_formatter("python_naive_dist value:", python_naive_dist(alist,blist))
print_formatter("python_naive_dist time (ms):", 1000*secs)

â

The pure Python implementation takes about ~790 ms to run. Take a note of the Euclidean distance value of 1290.91809, weâll use that to verify that the subsequent implementations are numerically accurate.

#### Python + NumPy implementation

To be fair to Python, rarely do Python programmers use Python native data structures for machine learning and scientific computing. The de facto standard for such use cases is the NumPy package, which provides the n-dimensional array data structure and optimized functions that operate on them. Since we already created a random NumPy vector in the previous step, weâll use the same numpy arrays and calculate the euclidean distance using NumPyâs vectorized numpy.linalg.norm function that computes the norm on the difference vector. We measure the execution time of the NumPy implementation below.

%%python
# Numpy's vectorized linalg.norm implementation 
def python_numpy_dist(a,b):
    return np.linalg.norm(a-b)

secs = timeit(lambda: python_numpy_dist(anp,bnp), number=5)/5
print("=== Python+NumPy Performance ===")
print_formatter("python_numpy_dist value:", python_numpy_dist(anp,bnp))
print_formatter("python_numpy_dist time (ms):", 1000*secs)

â

The time it took to calculate the Euclidean distance to the exact same value of 1290.91809 went from ~790 ms to ~24 ms: thatâs about 30 times faster using NumPyâs faster C/C++ implementation under the hood.

Can we run it faster with Mojo? Letâs find out!

#### Our first Mojo implementation

Mojo offers Pythonâs usability with optional low-level control like C. Letâs start with a Python-like implementation in Mojo and see what performance we get. First, we need a data structure for our vectors. Mojo offers a Tensor data structure which allows us to work with n-dimensional arrays, and for this example weâll create two 1-dimensional Tensors and copy over the NumPy array data to it.

from Tensor import Tensor
from DType import DType
from Range import range
from SIMD import SIMD
from Math import sqrt
from Time import now

let n: Int = 10_000_000
var a = Tensor[DType.float64](n)
var b = Tensor[DType.float64](n)

for i in range(n):
    a[i] = anp[i].to_float64()
    b[i] = bnp[i].to_float64()

â

Letâs dissect this piece of Mojo code. First, you'll notice that we have new variable declarations let and var which may look odd at first glance since this is not familiar Python syntax. Mojo offers optional (except in some cases, more on that later) variable declarations to declare variables as immutable with let (i.e. cannot be modified after creation) or mutable with var (i.e. can be modified). There are two benefits to using variable declarations - type safety and performance. Second, youâll also notice that the Tensor function has both square brackets and round brackets () with this format:

Function[parameters](arguments)

In Mojo "parameters" represent a compile-time value. In this example weâre telling the compiler, Tensor is a container for 64-bit floating point values. And arguments in Mojo represent runtime values, in this case weâre passing n=10000000 to Tensorâs constructor to instantiate a 1-dimensional array of 1 million values.

Finally, in the for-loop we assign numpy array values to Mojo Tensor. Weâre now ready to calculate the Euclidean distance measure in Mojo.

âCalculating the Euclidean distance in Mojo

Letâs bring our Python example over to Mojo and make a few changes to it. Below is our Mojo function for calculating Euclidean distance. Can you spot the few key differences vs. the Python function?

def mojo_naive_dist(a: Tensor[DType.float64], b: Tensor[DType.float64]) -> Float64:
    var s: Float64 = 0.0
    n = a.num_elements()
    for i in range(n):
        dist = a[i] - b[i]
        s += dist*dist
    return sqrt(s)

â

Notice that this is very similar to our Python code, except that weâve added types in the function arguments: a: Tensor[DType.float64], b: Tensor[DType.float64] and return type Float64. Unlike Python, Mojo is a compiled language and even though you can still use flexible types like in Python, Mojo lets you declare types so the compiler can optimize the code based on those types, and improve performance.

Here DType.float64 parameter of our Tensor specifies that it contains 64-bit floating point values. Float64 return type represents a Mojo SIMD type, which is a low-level scalar value on the machine register. We also declare the variable s with the var keyword which tells the Mojo compiler that s is a mutable variable of type Float64. Now weâre ready to benchmark our Mojo code.

let eval_begin = now()
let naive_dist = mojo_naive_dist(a, b)
let eval_end = now()

print_formatter("mojo_naive_dist value", naive_dist)
print_formatter("mojo_naive_dist time (ms)",Float64((eval_end - eval_begin)) / 1e6)

â

The execution time dropped down to ~70 ms from ~790 ms in pure Python, thatâs about 11x faster. However, that is still slower than Python+NumPyâs ~40 ms but pretty good without having to re-write our function in C/C++. But weâre not done yet! Weâre leaving a lot of performance on the table that we can recover with a few more minor code changes. Letâs see how.

#### Speeding up our Mojoð¥ code!

Just like in Python, def functions in Mojo are dynamic, flexible and types are optional which makes it easier to port Python functions to Mojo. However, there are a few key differences in how arguments are processed. In Python arguments to functions are references to objects and if modified, their changes are visible outside the function. In Mojo, def functions make a copy of all arguments and this introduces an overhead when dealing with large Tensors like we are. Therefore, to speed up our code further we need to:

- Pass Tensor values by reference so no copies are made

- Introduce strict typing and declare all variablesÂ

Hereâs our updated function that addressed both (1) and (2)

fn mojo_fn_dist(a: Tensor[DType.float64], b: Tensor[DType.float64]) -> Float64:
    var s: Float64 = 0.0
    let n = a.num_elements()
    for i in range(n):
        let dist = a[i] - b[i]
        s += dist*dist
    return sqrt(s)

â

The first change youâll notice is that the def has been replaced by fn. In Mojo, fn functions enforce strict type checking and variable declarations. The default behavior of fn is that arguments and return values must contain types and fn arguments are immutable variables. While def allows you to write more dynamic code, fn functions can improve performance by lowering overhead of figuring out data types at runtime and helps you avoid a variety of potential runtime errors. You can read more about the difference between fn and def in the Mojo programming manual.

Since all variables in fn functions have to be declared, we also declare n and dist with let and weâre ready to benchmark our updated code.

let eval_begin = now()
let naive_dist = mojo_fn_dist(a, b)
let eval_end = now()

print("=== Mojo Performance with fn, declarations and typing and ===")
print_formatter("mojo_fn_dist value", naive_dist)
print_formatter("mojo_fn_dist time (ms)",Float64((eval_end - eval_begin)) / 1e6)

â

Our Mojo code execution time dropped down to ~13 ms. Thatâs almost 2x faster than the NumPy which is implemented in C/C++ and 60x faster than the pure Python implementation. Letâs take a look at the Python and Mojo code side by side so you can appreciate how little you had to change the code to see the performance improvements.

### Conclusion

There is a lot more to discuss about Mojo. For now, I hope you found this blog post to be a quick and gentle introduction to Mojo from a Python programmerâs perspective. There are more things to try to speed up our code, including better ways to allocate memory, vectorization, multi-core parallelization, and more.Â  explore these topics in upcoming blog posts. The full Jupyter notebook is available on Mojo Playground â head over to the Playground and run the example yourself!Â

Now itâs your turn! How would you improve this code? Do you have ideas for other examples? Weâd love to hear from you! Join our awesome community on Discord and share your Mojo journey with us on social media. Until next time ð¥!

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Developer

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Shashank Prasanna

AI Developer Advocate

Shashank is an engineer, educator and doodler. He writes and talks about machine learning, specialized machine learning hardware (AI Accelerators) and AI Infrastructure in the cloud. He previously worked at Meta, AWS, NVIDIA, MathWorks (MATLAB) and Oracle in developer relations and marketing, product management, and software development roles and hold an M.S. in electrical engineering.



================================================================================

URL: https://www.modular.com/blog/weve-raised-100m-to-fix-ai-infrastructure-for-the-worlds-developers

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

August 24, 2023

# Weâve raised $100M to fix AI infrastructure for the world's developers

Modular Team

We are excited to announce that we have raised $100 million in new funding, led by General Catalyst and filled by existing investors GV (Google Ventures), SV Angel, Greylock, and Factory. This second round of funding follows our first $30 million round from last year and will enable us to supercharge our vision for the future of AI infrastructure for the world's developers. A special thank you to everyone who has helped us get to this point, including our incredible world-class team, our dedicated design partners, and our vibrant developer community and enterprise customers who believe in a better future of AI developer infrastructure.

âDeep Nishar, Managing Director at General Catalyst, is beyond excited:

## A better AI-powered world for everyone

Modularâs vision is to allow AI technology to be used by anyone, anywhere. Our next-generation AI developer platform enables our customers and the world's developers to defragment their AI technology stacks, pushing more innovations into production faster and realizing more value from their investment in AI. Our products and technologies improve the nature of AI programmability, usability, scalability, compute efficiency, and hardware utilization. And our infrastructure seamlessly integrates into our customers' environments with minimal migration cost, natively supporting multiple clouds, multiple hardware accelerators, and multiple AI frameworks out of the box. We are attacking the complexity that slows AI development today by solving the fragmentation issues that plague the AI stack, starting with where AI software meets AI hardware.

## The Modular AI Engine and Mojo ð¥

In the four months since our product keynote, the Modular community has grown to more than 120K+ developers (including tens of thousands of enterprises) who are excited to utilize our AI infrastructure stack. We have built and delivered the world's fastest unified AI engine that provides full compatibility with major AI frameworks â such as TensorFlow and PyTorch â and delivers unparalleled performance and cost savings on todayâs CPUs, with support for GPUs coming in the Fall. We also built Mojo ð¥, a programming language for all AI developers that is 35,000x faster than Python. Mojo combines the usability of Python and the performance of C, bringing programmability back to AI and providing a common interface for the AI hardware landscape. And together, we expect them to unlock the next wave of AI innovations.

## Mojo ð¥: coming soon to a computer near you!

We are also excited to announce the next major step forward for Mojo â the ability to download Mojo and a whole developer toolset locally to your desktop machine. The ability to run Mojo locally is the most requested feature from Mojo developers so far and will lead to increased innovation and collaboration in the Mojo developer ecosystem. We are shipping a preview of the Mojo SDK to hundreds of early adopters today, and we will release it generally to everyone in early September. Sign up to download.

Looking ahead, we are committed to releasing the Mojo standard library in open source and accepting contributions before the end of the year. Sign up to our Discord, and join our GitHub community to become a Mojician ð¥.

## The future of AI is bright

We founded Modular with a vision to recreate AI infrastructure for the world, with a hope that it acts as a catalyst to unlock more AI developers, more innovation, and ultimately better AI products for everyone. This is a huge and important mission, and now we have incredible resources to work towards that. We ask that you join us, use our infrastructure, and help create an incredible future with AI. Letâs change the world together!

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Company

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Modular Team

Company

Our mission is to have real, positive impact in the world by reinventing the way AI technology is developed and deployed into production with a next-generation developer platform.



================================================================================

URL: https://www.modular.com/blog/mojo-python-calculating-and-plotting-a-valentines-day-using-mojo-and-python

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

February 15, 2024

# Mojoð¥ â¥ï¸ Python: Calculating and plotting a Valentineâs day â¥ï¸ using Mojo and Python

Shashank Prasanna

On Valentineâs Day yesterday, I wanted to create something special to celebrate my love for Mojo and Python. My search on the interwebs led me to a nifty little equation that plots a heart. The equation is quite simple and Iâll refer to this as the âheart equationâ through the rest of this blog post:

$f(x) = (x)^{\frac{2}{3}} - 0.9 \sqrt{3.3 - x^2} \sin(a \pi x)$

In this equation if you vary the variable a from 0 to 20 in small increments, you get this cool animation:

The heart equation is fairly easy to implement in Python using NumPy, but in this blog post I'll show you how you can implement it in Mojoð¥. Mojo is still young and its standard library doesnât yet have a robust NumPy-like data structure for implementing math equations, but with relatively low effort you can implement a custom data structure (Iâll call MojoArray in this blog post) that supports basic vectorized math operations we need, to implement the heart equation. Once we've created our MojoArray data structure, we can implement the equation and plot it using the following code:

Since Mojo offers first-class support for Python interoperability, weâll see how you can do all the computation in Mojoð¥ and leverage Python and Matplotlib library to plot the animation you see above.

### What youâll learn:

- How to create a custom Mojoð¥ data structure that lets do vectorized math operations

- How to use double underscore aka dunder methods to overload math operators that make writing math equations easy

- How to use Mojo standard library Math functions that operate on SIMD type and extend them to work on your data structure

- How to leverage Python libraries to import NumPy arrays and plot using Matplotlib

### Creating an Array data structure that supports vectorized Math operations

To implement the heart equation, I need a data structure that supports vectorized Math operations. For example, in this equation x is a vector, and I need sqrt, pow, sin, add, mul and div to operate on vectors.

$f(x) = (x)^{\frac{2}{3}} - 0.9 \sqrt{3.3 - x^2} \sin(a \pi x)$

Mojo Math module in the standard library supports common mathematical operations but they operate on SIMD types only. SIMD types allow you perform a single operation on small vectors whoâs length depends on the CPU type. For example, to compute sin on a SIMD vector, in Mojo you can run:

Output:

Which gives you very small numbers as youâd expect, since sin of approximate value of pi, is approximately 0. To implement the heart equation, we'll need to work with large vectors, therefore, we need a Mojo struct that offers methods to perform Math operations on large vectors, not just SIMD types.

Mojo standard library offers a Tensor data structure that can store higher order tensors, but it is quite an overkill for this simple example. The Tensor type also doesnât support basic Math operations which we need. With a simple, custom data structure called MojoArray, we can only implement the features we need.

Here is the skeleton of the MojoArray data structure that weâll be using to calculate our heart equation.Â

The MojoArray data structure is parameterized on dtype which defaults to float64, which is also the default type of NumPy arrays. MojoArray struct also defines double underscore aka dunder methods that overload common mathematical operations such as addition, subtraction, multiplication to work on two MojoArrays or MojoArray and scalar values.

Letâs take a closer look at the implementation details of a few of these. Most of the math operations fall into one of these categories:

#### Elementwise transforms: _elemwise_transform[]()

This helper function implements Trigonometric functions, and abs. that transform a MojoArray. _elemwise_transform[]() applies the function func on all the elements in the MojoArray.

I use _elemwise_transform to implement sqrt, cos, sin and abs:

#### Elementwise vector-scalar operations _elemwise_scalar_math[]():Â

This helper function implements multiplication, addition, and subtraction between MojoArrays and scalars. _elemwise_array_math accepts a scalar and applies the function func on all the elements in the MojoArray.

We use _elemwise_scalar_math to implement negative, addition, subtraction, and multiplication between a vectors and a scalar:

#### Elementwise vector-vector operations _elemwise_array_math[]()

This helper function implements multiplication, addition, and subtraction between two MojoArrays.Â

We use _elemwise_array_math to implement addition, subtraction, and multiplication between two vectors

### Implementing the Heart Equation

With our MojoArray data structure in place, weâre now ready to implement our heart equation. In the main() function, we perform the following steps:

- Load Python modules NumPy and Matplotlib

- Create NumPy arrays and use that to instantiate our MojoArray data structure

- Implement the heart equation y = (x**2)**(1/3.) - 0.9*((3.3-(x*x)).sqrt())*(a[i]*3.14*x).sin()

- Loop over values of a to create the animation I showed at the top of the blog post.

## Conclusion

Happy Valentinesâ week! Hope you enjoyed reading this blog post on how to implement the heart equation in Mojo. My hope is that you can reuse parts of this example in your own workflows. Download Mojo to run this example and share your feedback with us! Here are some additional resources to get started:

- Get started with downloading Mojo

- Head over to the docs to read the programming manual and learn about APIs

- Explore the examples on GitHub

- Join our Discord community

- Contribute toÂ  discussions on the Mojo GitHub

- Read and subscribe to Modverse NewsletterÂ

- Read Mojo blog posts, watch developer videos and past live streams

- Report feedback, including issues on our GitHub tracker

Until next timeð¥!

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Developer

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Shashank Prasanna

AI Developer Advocate

Shashank is an engineer, educator and doodler. He writes and talks about machine learning, specialized machine learning hardware (AI Accelerators) and AI Infrastructure in the cloud. He previously worked at Meta, AWS, NVIDIA, MathWorks (MATLAB) and Oracle in developer relations and marketing, product management, and software development roles and hold an M.S. in electrical engineering.



================================================================================

URL: https://www.modular.com/#__docusaurus_skipToContent_fallback

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

80K+ developers building with MAX

# A high-performance generative AI framework

MAX replaces and simplifies your AI inference stack, maximizing performance and reducing cloud costs

## Achieve state of the art NVIDIAÂ GPU performance

Unlock state of the art latency and throughput without writing low-level CUDA code.

### Deploy MAX on GPUs

## Optimize your existing PyTorch & ONNX models

Migrate seamlessly without rewriting your AI models and pipelines on a unified AI stack.

## Use Mojo to supercharge your AIÂ applications

Extend your PythonÂ code with high-performance Mojo, a new programming language with the expressiveness of Python and the performance of C.

## Develop locally, deploy globally to any cloud.

Develop your AI applications locally and package and deploy across any cloud provider, on CPUs and GPUs, without having to change your code.

## Run Gen AI locally underÂ 3Â minutes

Download MAX first to run an LLM

- Run Llama3-8B in your CLIGet started

- Run Llama2-7B in your CLIGet started

- Run Replit-3B in your CLIGet started

Run Llama3-8B in your CLI

Run Llama2-7B in your CLI

Run Replit-3B in your CLI

## Free Forever

The MAXÂ framework is a free and open platform for you to develop and deploy AI inference workloads.

A new framework for Gen AI, and the best way to deploy PyTorch

Development tools for accelerated compute on GPUs and CPUs, built from the ground up for GenAI, but compatible with today.

## Developer Approved ð

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

## Get started now

- Getting started guideStep by step walkthrough on how to get started

- Browse examplesRun a project locally in just 2min

- Connect with buildersJoin over 22,000 developers

Getting started guide

Step by step walkthrough on how to get started

Browse examples

Run a project locally in just 2min

Connect with builders

Join over 22,000 developers

Download for your platformÂ now

- 

- 

- 

View Pricing

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

MAX on GPU waitingÂ list

Be the first to get lightning fast inference speed on your GPUs. Be the envy of all your competitors and lower your compute spend.

================================================================================

URL: https://www.modular.com/about

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# Build AI for anyone, anywhere.

## Our mission is to have real, positive impact in the world by reinventing the way AI technology is developed and deployed into production with a next-generation developer platform.

We believe that AI is a net positive force in the world and will help transform it for the better.

Our vision and mission are to reinvent AI infrastructure to advance humanity.

### We aim to push the whole world of AI forward, not just aÂ select few companies andÂ products.

After working for years at the worldâs largest technology companies, scaling the world's largest AI work loads, building the hardware that powers them, and deploying AI to billions of mobile phones and edge devices, we saw that fragmentation and technical complexity held back the impact to a privileged few.

We need to rethink the current AI systems and infrastructure from first principles to make it easy for anyone to leverage AI to solve the worldâs most critical problems with software that just works.

Modular and composable infrastructure that simplifies AI development and deployment is what the world needs.

Together, as Modulers, we are a team of the world's best AI infrastructure leaders who are reinventing and rebuilding accelerated compute for everyone.

### How it started

Chris Lattner and Tim Davis met at Google and felt AI was being held back by overly complex and fragmented infrastructure. Motivated by a desire to accelerate the impact of AI on the world by lifting the industry towards production-quality AI software, they founded Modular.

Leadership team

Chris Lattner

Co-Founder & CEO

Tim Davis

Co-Founder & President

Adin Scannell

Cloud Infrastructure Lead

Eric Johnson

Product Lead

Joe Pamer

Mojo Distinguished Engineering Lead

Kalor Lewis

VP, Finance

Mike Edwards

Operations Lead

Mostafa Hagog

Performance & Compiler Engineering Lead

Nick Kreeger

Framework Engineering Lead

## Backed by the best investors in AI

Our goal is as enormous as it is profound. Weâre building a different kind of company to achieve this.

We have assembled the best AI software and hardware leaders, and are systematically rebuilding the AI software stack from the ground up.

ð Weâre hiring!

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Chris Lattner

Co-Founder & CEO

Distinguished Leader who founded and scaled critical infrastructure including LLVM, Clang, MLIR, Cloud TPUs and the Swift programming language. Chris built AI and core systems at multiple world leading technology companies including Apple, Google, SiFive and Tesla.

clattner@modular.com

Tim Davis

Co-Founder & President

Repeat Entrepreneur and Product Leader. Tim helped build, found and scale large parts of Google's AI infrastructure at Google Brain and Core Systems from APIs (TensorFlow), Compilers (XLA & MLIR) and runtimes for server (CPU/GPU/TPU) and TF Lite (Mobile/Micro/Web), Android ML & NNAPI, large model infrastructure & OSS for billions of users and devices. Loves running, building and scaling products to help people, and the world.

tim@modular.com

Adin Scannell

Cloud Infrastructure Lead

As a hands-on technical leader, Adin is passionate about building products and technologies that challenge the status quo. He previously co-founded GridCentric which was acquired by Google, where he worked on problems at scale for nine years across Google and Google Cloud. He has spent the last two decades solving problems across the infrastructure stack â from hypervisors and operating systems to containers and security.



Eric Johnson

Product Lead

Product leader who has built and scaled AI applications and infrastructure. Eric led the TensorFlow API, Compiler, and Runtime teams at Google Brain and Core Systems, including the founding of TFRT and the productionization of JAX. He holds an MBA from Wharton and Computer Science MS from Penn and loves soccer, fitness, and the great outdoors.

eric@modular.com

Joe Pamer

Mojo Distinguished Engineering Lead

Joe spent the last 8 years at Meta, where he was focused on improving the companyâs AI and developer infrastructure stacks. Before that he served as Instagramâs CTO, overseeing much of Metaâs work on programming languages (mainly focused on Python, PHP, and Javascript), and co-founded its first applied ML team for infrastructure. Before joining Facebook, he helped design and develop the F#, TypeScript, and Swift programming languages, and contributed to many other developer technologies, such as Clang, Visual Studio, and VS Code.



Kalor Lewis

VP, Finance

Kalor is Modular's VP, Finance and leads all our Finance operations. Prior to Modular, Kalor was a VP, Finance at Fivetran where he was the first finance hire in 2018 and built out the companies entire finance function. Before Fivetran, Kalor was part of Palantir Technologies, where he scaled their strategic finance function.



Mike Edwards

Operations Lead

Mike has spent over 25 years working in the fields of IT, corporate operations, and software development - most recently at Apple. Mike volunteers his time serving as a Board member with the LLVM Foundation, focusing on finance and operations. Mike truly believes in the power of AI to help address some of the worldâs greatest needs.



Mostafa Hagog

Performance & Compiler Engineering Lead

Mostafa is a seasoned engineering leader in high-performance computing. During his tenure at NVIDIA, he served as Engineering Director and led teams to develop optimized deep learning libraries like cuDNN and CUTLASS, revolutionizing GPU-accelerated AI. At SiFive, as VP of Software, Mostafa assumed a leadership role guiding teams in the development of an MLIR/LLVM-based software stack for SiFive Intelligence & performance cores. His contributions also extend to optimizing Intel GPU hardware/software features, playing a pivotal role in developing the AVX1/2 SIMD ISA for Intel CPUs, and contributing to the GNU C Compiler. Mostafa holds a Master of Science in Electrical Engineering from the Technion, with a specialization in compiler optimizations. His unwavering passion for innovation continues to drive advancements in the field of high-performance computing.



Nick Kreeger

Framework Engineering Lead

Software Engineering lead with over 15 years of experience working at Google, Microsoft and a handful of startups. Nick has contributed to many technologies in Machine Learning such as TensorFlow.js, TensorFlow Lite/Micro, and ONNX/ONNXRuntime. Nick enjoys spending his free time with family and enjoying the Minnesotan outdoors.



================================================================================

URL: https://www.modular.com/max/tutorials/get-started-with-max-graph

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# MAX empowers you to own &Â control your AI

A new framework for Gen AI, and the best way to deploy PyTorch

## Unparalleled GenAI performance

- Llama3 - Optimized pipelinesView on Github





Llama3.pyllama 3 ../run-pipeline.ð¥ llama3 --max-tokens 200 --prompt "Why 
the sky is blue?" --quantization-encoding q4_0

Loading tokenizer...
Building model...
Compiling...
Executing...

The sky appears blue due to a phenomenon called Rayleigh scattering...

Prompt size: 10
Output size: 190

- Deploy LLMs with a single commandCheckout MAX Builds

### Llama3 - Optimized pipelines

llama 3 ../run-pipeline.ð¥ llama3 --max-tokens 200 --prompt "Why 
the sky is blue?" --quantization-encoding q4_0

Loading tokenizer...
Building model...
Compiling...
Executing...

The sky appears blue due to a phenomenon called Rayleigh scattering...

Prompt size: 10
Output size: 190

## Deploy LLMs with a single command

## The best way to deploy PyTorch

- SOTA performance in just 3 lines of codeDrop in your PyTorch or ONNX models and get an instant boost in performance with our next generation inference runtime for CPUs and GPUsSee for yourself

## SOTA performance in just 3 lines of code

Drop in your PyTorch or ONNX models and get an instant boost in performance with our next generation inference runtime for CPUs and GPUs

## Compatible with what you use today

- Supports all your use cases and existing toolsUse the MAX APIs to build, optimize and deploy from one model to more complex GenAI pipelines on CPUs or GPUs.Supported model formats

## Supports all your use cases and existing tools

Use the MAX APIs to build, optimize and deploy from one model to more complex GenAI pipelines on CPUs or GPUs.

## Build locally. Deploy easily across hardware in the cloud

- Compute Abstraction for AIBuild your AI applications, package and deploy across CPUs and GPUs platforms including Apple, ARM, Intel, AMD and NVIDIA without code changes.Supported hardware

- Accelerate your time to marketÂ with MAXÂ onÂ AWSGet help from the experts using a production gradeÂ managed service on AWSLearn more

## Compute Abstraction for AI

Build your AI applications, package and deploy across CPUs and GPUs platforms including Apple, ARM, Intel, AMD and NVIDIA without code changes.

## Accelerate your time to marketÂ with MAXÂ onÂ AWS

Get help from the experts using a production gradeÂ managed service on AWS

## Develop with Python, Â ExtendÂ withÂ Mojoð¥

- Use what you know with Python APIs in MAXUse our Python integration to interop with your existing workloads and offloads onto MAX where it mattersUsing Python with MAX

- Learn how to scale your AI with MojoNo need to learn C and CUDA, use Mojo the easiest way to program CPUs and GPUs.Take a tour of Mojoð¥

## Use what you know with Python APIs in MAX

Use our Python integration to interop with your existing workloads and offloads onto MAX where it matters

## Learn how to scale your AI with Mojo

No need to learn C and CUDA, use Mojo the easiest way to program CPUs and GPUs.

## What developers are saying about MAX

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

âThe Community is incredible and so supportive. Itâs awesome to be part of.â

benny.n

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

âThe Community is incredible and so supportive. Itâs awesome to be part of.â

benny.n

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

âThe Community is incredible and so supportive. Itâs awesome to be part of.â

benny.n

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

âThe Community is incredible and so supportive. Itâs awesome to be part of.â

benny.n

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

## Get started now

- Getting started guideStep by step walkthrough on how to get started

- Browse examplesRun a project locally in just 2min

- Connect with buildersJoin over 22,000 developers

Getting started guide

Step by step walkthrough on how to get started

Browse examples

Run a project locally in just 2min

Connect with builders

Join over 22,000 developers

Download for your platformÂ now

- 

- 

- 

View Pricing

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/mojo/manual/

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/cli/

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/examples/

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/api/

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/max-pipeline-bring-your-own-model

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/deploy-aws-kubernetes

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/deploy-cloudformation-sagemaker

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/onnx-custom-op

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/run-onnx-with-python

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/graph

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/api/mojo/graph/symbol/Symbol

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/extensibility/graph-custom-op

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/get-started-with-max-graph#set-up-max

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/install

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/mojo/manual/basics

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/roadmap

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/get-started-with-max-graph#build-a-hello-world-graph

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/get-started-with-max-graph#1-build-the-graph

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/api/mojo/graph/graph/Graph

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/api/mojo/graph/graph/Graph#verify

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/get-started-with-max-graph#2-create-inference-session-load-and-compile-the-graph

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/api/mojo/max/session/InferenceSession

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/api/mojo/max/model/Model

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/get-started-with-max-graph#3-execute-the-graphmodel-with-inputs

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/mojo/stdlib/tensor/tensor/

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/api/mojo/max/model/Model#execute

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/api/mojo/max/tensor_map/TensorMap

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/api/mojo/max/tensor_map/TensorMap#get

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/get-started-with-max-graph#build-a-matmul-graph

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/api/mojo/graph/type/Dim

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/get-started-with-max-graph#build-an-mnist-classifier-graph

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/get-started-with-max-graph#1-build-and-train-the-model-in-pytorch

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/get-started-with-max-graph#2-build-the-inference-graph-with-max-graph

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/api/mojo/graph/ops/elementwise/softmax

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/get-started-with-max-graph#3-run-inference-and-check-accuracy

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/mojo/stdlib/algorithm/reduction/argmax

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/get-started-with-max-graph#create-a-custom-operator-with-max-graph

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/get-started-with-max-graph#1-implement-the-custom-op

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/api/mojo/extensibility/Tensor

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/api/mojo/extensibility/empty_tensor

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/api/mojo/extensibility/Tensor#for_each

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/get-started-with-max-graph#2-add-the-custom-op-to-the-graph

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/api/mojo/graph/ops/custom_ops/custom

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/get-started-with-max-graph#3-verify-the-results

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/get-started-with-max-graph#4-deploy-as-a-binary

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/tutorials/get-started-with-max-graph#next-steps

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/mojo/manual

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/changelog

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/max/mojo

MAX on GPU waitingÂ list

Be the first to get lightning fast inference speed on your GPUs. Be the envy of all your competitors and lower your compute spend.

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# The expressiveness of Python, â¨with the performance of C

Mojo installs as part of MAX

- 175kMojo Developers

- 23kStars on Github

- 22kCommunity members

175k

Mojo Developers

23k

Stars on Github

22k

Community members

## Mojo has powerful &Â easyÂ to useÂ features

Leverage types for better performance and error checking.

def sort(v: ArraySlice[Int]):
  for i in range(len(v)):
    for j in range(len(v) - i - 1):
      if v[j] > v[j + 1]:
        swap(v[j], v[j + 1])

struct MyPair:
  var first: Int
  var second: F32
  
  def __init__(self, first: Int, second: F32):
    self.first = first
    self.second = second

def reorder_and_process(owned x: HugeArray):
  sort(x)	# Update in place
  
  give_away(x^)	# Transfer ownership
  
  print(x[0])	# Error: âxâ moved away!

def exp[dt: DType, elts: Int]
    (x: SIMD[dt, elts]) -> SIMD[dt, elts]:
  x = clamp(x, -88.3762626647, 88.37626266)
  k = floor(x * INV_LN2 + 0.5)
  r = k * NEG_LN2 + x
  return ldexp(_exp_taylor(r), k)

def exp_buffer[dt: DType](data: ArraySlice[dt]):

  # Search for the best vector length
  alias vector_len = autotune(1, 4, 8, 16, 32)
  
  # Use it as the vectorization length
  vectorize[exp[dt, vector_len]](data)

def sort(v: ArraySlice[Int]):
  for i in range(len(v)):
    for j in range(len(v) - i - 1):
      if v[j] > v[j + 1]:
        swap(v[j], v[j + 1])

struct MyPair:
  var first: Int
  var second: F32
  
  def __init__(self, first: Int, second: F32):
    self.first = first
    self.second = second

def reorder_and_process(owned x: HugeArray):
  sort(x)	# Update in place
  
  give_away(x^)	# Transfer ownership
  
  print(x[0])	# Error: âxâ moved away!

def exp[dt: DType, elts: Int]
    (x: SIMD[dt, elts]) -> SIMD[dt, elts]:
  x = clamp(x, -88.3762626647, 88.37626266)
  k = floor(x * INV_LN2 + 0.5)
  r = k * NEG_LN2 + x
  return ldexp(_exp_taylor(r), k)

def exp_buffer[dt: DType](data: ArraySlice[dt]):

  # Search for the best vector length
  alias vector_len = autotune(1, 4, 8, 16, 32)
  
  # Use it as the vectorization length
  vectorize[exp[dt, vector_len]](data)

- Progressive types

- Zero cost abstractions

- Ownership + borrow checker

- Portable parametric algorithms

- Language integrated auto-tuning

Progressive types

Zero cost abstractions

Ownership + borrow checker

Portable parametric algorithms

Language integrated auto-tuning

Progressive types

Zero cost abstractions

Ownership + borrow checker

Portable parametric algorithms

Language integrated auto-tuning

Extend Python or scale all the way down to the metal. Program the multitude of low-level AI hardware. No C++ or CUDA required.

Using Mojo with MAX unlocks the future of accelerated compute for the world

Development tools for accelerated compute on GPUs and CPUs, built from the ground up

## Mojoð¥ + MAX unlock incredible NVIDIA GPU performance

Mojo with MAX enables state of the art latency and throughput without writing low-level CUDA code.

### Deploy MAX on GPUs

## See what AI developers are building withÂ Mojoð¥

LLM.mojo

A implementation of Andrej Karpathy's llm.c to Mojo ð¥

Basalt

A machine learning framework in pure Mojo ð¥

Lightbug HTTP

A HTTP web framework written in pure Mojo ð¥

Endia

A dynamic Array library for Scientific Computingð¥

+300

more open source projects

## Developers love MojoÂ ð¥

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

Mojo  destroys Python in speed.  12x faster without even trying. The future is bright!

mytechnotalent

âThe Community is incredible and so supportive. Itâs awesome to be part of.â

benny.n

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ



âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

## Unlock Python performance

Utilize the full power of the hardware, including multiple cores, vector units, and exotic accelerator units, with the world's most advanced compiler and heterogenous runtime. Achieve performance on par with C++ and CUDA without the complexity.

- PythonSingle Thread Distribution

- Mojoð¥Parallel processing across multiple cores

### Python

Single Thread Distribution

### Mojoð¥

Parallel processing across multiple cores

## Mojo enables systems programming features so you can process information exponentiallyÂ faster.

0.03s

68,000x

* Algorithm

Mandelbrot

Instance

AWS C1.xlarge

Intel Xeon

Read our 68,000x blog post

## Access the entire PythonÂ ecosystem

Experience true interoperability with the Python ecosystem. Seamlessly intermix arbitrary libraries like Numpy and Matplotlib and your custom code with Mojo.

- 

- Mojo ð¥

Mojo ð¥

Python

def make_plot(m: Matrix):
  plt = Python.import_module("matplotlib.pyplot")
  fig = plt.figure(1, [10, 10 * yn // xn], 64)
  ax = fig.add_axes([0.0, 0.0, 1.0, 1.0], False, 1)
  plt.imshow(image)
  plt.show()

make_plot(compute_mandelbrot())

## Upgrade your models toÂ fullÂ pipelines with MAX

Easily extend your models with pre and post- processing operations, or replace operations with custom ones. Take advantage of kernel fusion, graph rewrites, shape functions, and more.

## Get started now

- Getting started guideStep by step walkthrough on how to get started

- Browse examplesRun a project locally in just 2min

- Connect with buildersJoin over 22,000 developers

Getting started guide

Step by step walkthrough on how to get started

Browse examples

Run a project locally in just 2min

Connect with builders

Join over 22,000 developers

Download for your platformÂ now

- 

- 

- 

View pricing

Join the community

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog/mojo-vs-rust-is-mojo-faster-than-rust

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

February 12, 2024

# Mojo vs. Rust: is Mojo ð¥ faster than Rust ð¦ ?

Jack Clayton

Mojo is built on the latest compiler technology in MLIR, an evolution of LLVM which Rust lowers to,Â and so it can be faster. It depends largely on the skill of the programmer and how far they're willing to go with optimizations. Mojo's goal as a language, is to meet Python developers where they are, and allow them to learn some new tricks to optimize their code to the performance limits of any hardware.

### Blogs and Benchmarks

Over the weekend, Netflix engineer and Rust advocate @ThePrimeagen released a reaction video to a community guest blog we published: Outperforming Rust âï¸ DNA sequence parsing benchmarks by 50% with Mojo ð¥. The blog post stirred up some controversy, Rust is being positioned as a potential successor to the dominant languages in AI, which are currently Python and C++. This was @ThePrimeagen's take on Mojo vs. Rust for the future of AI programming:

Following his comment, Luca Palmieri, respected Rustacean and author of Zero to Production in Rust, replied on X:

### Mojo:Â our goal

Mojo aims to be intuitive to learn for Python developers. As Mohamed showed, he was able to learn Mojo and optimize an algorithm using SIMDÂ in a matter of weeks as a side project. While this article is focused on performance differences, the points that @ThePrimeagen and LucaÂ Palmieri made are important to us. We are heavily focused on AI, where a three-language problem exists, and where CPU+GPU programmability is so important across hardware. But lets not forget, the real goal of Mojo is lifting the worlds most popular AI language in Python, and empowering developers everywhere with incredible performance, hardware portability, and programmability.

### Is Mojo faster than x language?

@ThePrimeagen raised an important question: Rust is known for low-level performance, how can Mojo provide better performance out of the box than Rust (and C++)?

A common question when users first join the Discord is How much faster is Mojo than x language?. There are a lot of considerations surrounding any benchmark implementation, you can't use any one benchmark to say x language is faster than y language. A better question is How much overhead does Mojo introduce, compared to x?. A major goal for Mojo, is to allow you to push hardware to the limits of physics, while remaining ergonomic and familiar to Python developers.

Compared to a dynamic language like Python, compiled languages allow you to remove unnecessary CPU instructions such as allocating objects to the heap, reference counting, and periodic garbage collection. Mojo takes lessons learned and best practices from C++, Rust and Swift to provide direct access to the machine without these kinds of overheads.

### Mojo vs. Rust

Mojo and Rust both allow you to optimize at a lower level, but in Rust for example you can still wrap everything in Arc, Mutex, Box etc. to avoid fights with the borrow checker at the cost of performance. If youâre writing application code this might not have any significant impact, but if youâre writing a library or performance sensitive code, that overhead can add up quickly. It's up to the programmer how much they care about reducing overhead and optimizing performance.

Both of the languages can use LLVM for optimized codegen, and both languages allow the use of inline assembly (but of course, no one can afford to do that) so they both have the same potential on traditional hardware right?

Well sure, but the real question is: how does the performance of idiomatic/normal Mojo code compare to normal Rust code written by someone who isnât a world expert writing in assembly for every chip, and doesnât know all the details of how the compiler works?

#### Reduced memcpy with borrow by default

When a new user is learning Rust, one of the first pitfalls they run into, is that function arguments default to taking an object by moving it. This means when you pass something into a function and try to reuse it, you get a compiler error:

The line with dbg! throws a compiler error, because you've moved foo into the bar function. In Rust this can also mean foo does a memcpy of the String pointer, size, and capacity. The memcpy can be optimized away by LLVM in some cases, but this doesn't always occur and is hard to predict unless you know how the Rust/LLVM compiler works.

Mojo simplifies this concept for the standard use case:

Mojo arguments are borrowed by default: not only is this much more gentle when learning Mojo compared to Rust, it's also more efficient due to no implicit memcpys. Â If you want to get closer to Rust behavior, you can change the argument to owned:

This still works! Because String implements a copy constructor, it's able to be moved into bar and leave behind a copy. Under the hood this is still passing by reference for maximum efficiency, it'll only create a copy if foo is mutated.

To fully opt into the Rust default of moving an object and losing ownership, you need to use the ^ transfer operator:

Now you finally get a compiler error for trying to use foo after move, you have to work much harder to fight the borrow checker in Mojo! This is the better default behavior, not only is it more efficient, it doesn't roadblock engineers from dynamic programming backgrounds. They still get the behavior they expect by default, with the best performance possible.

#### No Pin requirement

In Rust for a self-referential struct pointing to its own member, that data can become invalid if the object moves, as it'll be pointing to the old location in memory. This creates a complexity spike, particularly in parts of async Rust where futures need to be self-referential and store state, so you must wrap Self with Pin to guarantee it's not going to move. In Mojo, when moving an object that has an an address, you can still update any self-referential fields. So self.foo will continue to point correctly to the location of the object in memory, even in async contexts.

There is a nice blog titled pin and suffering that takes you on a journey of a Rustacean ð¦ working through the implications of Pin. These are complexities that a Mojician ðª will never encounter.

Update: In a recent blog by one of the core Rust async contributors: https://without.boats/blog/pin, they note that we previously mentioned value identity but didn't explain what it means anywhere, including in our docs. It's beyond the scope of this blog to explain the machinery of how Mojo enables self-referential objects in async contexts, but stay tuned for an upcoming deep dive on this.

#### Built on state-of-the-art compiler technology

Rust was started in 2006 and Swift was started in 2010, and both are primarily built on top of LLVM IR.Â Mojo started in 2022 and builds on MLIR, which is a more modern ânext generationâ compiler stack than the LLVM IR approach that Rust uses. There is a history here: our CEO Chris Lattner started LLVM in college in Dec 2000 and learned a lot from its evolution and development over the years. Â He then led the development of MLIR at Google to support their TPU and other AI accelerator projects, taking that learning from LLVM IR to build the next step forward: described in this talk from 2019.

Mojo is the first programming language to take advantage of all the advances in MLIR, both to produce more optimized CPU code generation, but also to support GPUs and other accelerators, and to also have much faster compile times than Rust. Â This is an advantage that no other language currently provides, and it's why a lot of AI and compiler nerds are excited about Mojo ð¥. They can build their fancy abstractions for exotic hardware, while us mere mortals can take advantage of them with Pythonic syntax.

#### Great SIMD ergonomics

CPUs have special registers and instructions to process multiple bits of data at the same time, known as SIMD (Single Instruction, Multiple Data). But the ergonomics of writing this code has historically been very ugly and difficult to use. These special instructions have been around for many years, but most code is still not optimized for it. When someone works through the complexities and writes a portable SIMD optimized algorithm, it blows the competition out of the water, for example simd_json.

Mojo's primitives are natively designed to be SIMD-first:Â UInt8 is actually a SIMD[DType.uint8, 1] which is a SIMD of 1 element. There is no performance overhead to represent it this way, but it allows the programmer to easily use it for SIMD optimizations. For example, you can split up text into 64 byte blocks and represent it as SIMD[DType.uint8, 64] then compare it to a single newline character, in order to find the index for every newline. Because the SIMD registers on your machine can calculate operations on 512bits of data at the same time, this will improve the performance for those operations by 64x!

Or a more simple example is if you have a SIMD[DType.float64, 8](2, 4, 6, 8, 16, 32, 64, 128), you can simply multiply it by a Float64(2), improving performance by 8x on most machines compared to multiplying each element individually.

LLVM (and therefore Rust) has automatic vectorization optimization passes, but theyâll never be able to reach the same level of performance as the programmer expressing exactly what they intended, because LLVM cannot change memory layout or other important details for SIMD. Mojo has been built from the ground up to take advantage of SIMD, and writing SIMD optimizations feels very close to writing normal code.

#### Eager Destruction

Rust was inspired by RAII (Resource Acquisition is Initialization) from C++, which means that once the object goes out of scope, the application developer doesn't have to worry about freeing the memory, the programming language takes care of it. This is a really nice paradigm, you get the ergonomics of a dynamic language, without the performance drawback of a garbage collector.

Mojo takes this one step further, instead of waiting until the end scope, it frees the memory on last use of the object. This is advantageous in the field of AI, where freeing an object early can mean deallocating a GPU tensor earlier, therefore fitting a larger model in GPU RAM. This is a unique advantage for Mojo, where the programmer gets the best possible outcome without having to think about it. The Rust borrow checker originally extended the lifetime of everything to the end of its scope to match the destructor behavior, which had some confusing consequences for users. Rust added features to simplify this for developers with Non-Lexical Lifetimes. Due to Mojoâs eager destruction, we get these simplifications for free, and it aligns with how objects are actually destroyed so we donât have confusing edge cases.

Another piece of overhead is the way that Drop works in Rust. It tracks if an object should be dropped at runtime, with Drop Flags. Rust can optimize these away in some cases, but Mojo defines them away categorically to eliminate the overhead in all cases.

#### Tail Call Optimization (TCO)

Update:Â It was pointed out in community discussion that for the original examples below Mojo was optimizing away everything, while Rust has a potential bug that was causing the implementation to be much slower. The generated assembly also shows Rust is doing some form of TCO, even with heap allocated objects. I've updated the below examples and reworded this section taking these points into consideration.

Because Mojo has eager destruction, MLIR and LLVM are able to perform tail call optimizations more effectively. This example compares a recursive function with a heap allocated dynamic vector in both languages. Note that this is just a simple example with as few lines of code as possible to demonstrate the difference.

First run cargo new rust and edit ./rust/src/main.rs to look like this:

Then run:

These results are on an M2 Mac:

And you can run the mojo version with a single file in the same folder, call it mojo.mojo:

Then run:

The compiler must ensure that destructors are called at the appropriate time, which for Rust is when a value goes out of scope. In the recursive function, the Vec has a destructor that needs to be run after each function call. This means the function's stack frame can't just be discarded or overwritten, as is required for tail call optimization. Because Mojo destructs eagerly it doesn't have this limitation, and is able to optimize for TCO more efficiently with heap allocated objects.

You can get more insight to this behaviour when profiling the two programs with valgrind --tool=massif. I switched to a Linux cloud instance to run this experiment, which sent the Rust mean time to 9.067 s with 10Â GB peak allocated memory, and Mojo to 1.189 s with 1.5 MB peak allocated memory! As previously noted, memory is in an important resource in AI applications, and eager destruction ensures the programmer gets optimal behaviour without having to think about it.

You can try running the above bechmarks yourself. If you don't have Mojo ð¥ yet, you can install it here!

### Conclusion

We all love Rust at Modular and are inspired by it, the tooling is great, and it currently has one of the best high level ergonomics for any systems programming language. But it has two major problems in the field of AI, which @ThePrimeagen pointed out:

- It compiles slow, and AI is all about experimentation and rapid iteration

- Most AI researchers experienced with Python won't take time to learn a new language from scratch

Members of our team tried to solve this problem with "Swift for TensorFlow" at Google, which didn't catch on due to the issues mentioned with AI researchers not willing to learn a brand new, slower to compile language. We love Python/C++/Rust/Swift/Julia etc. but after over a decade of the industry hill climbing these technologies, we believe that the fresh start that Mojo embodies is the only way to make a dent in these age-old problems.

Mojo already has optimal performance for systems engineers, but still has a long way to go for all the dynamic features that Python programmers expect. Rust is an excellent choice if you need to put something into production right now. If you're curious and looking towards the future, and want to be early with a language that could be instrumental to the next 50 years of AI, give Mojo a try! We'll be adding AI specific libraries to the package that comes with Mojo soon, which we're working on as the killer app to show the world what Mojo can do. Keep an eye out for MAX in the coming weeks!

We'd love to see you in the Mojo community, here are some links to get you started:

- Get started with downloading Mojo

- Head over to the docs to read the programming manual and learn about APIs

- Explore the examples on GitHub

- Join our Discord community

- Contribute to Â discussions on the Mojo GitHub

- Read and subscribe to Modverse Newsletter

- Read Mojo blog posts, watch developer videos and past live streams

- Report feedback, including issues on our GitHub tracker

â

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Engineering

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Jack Clayton

AI Developer Advocate

Jack started his career optimizing autonomous truck software for leading mining companies, including BHP and Caterpillar. Most recently he was designing computer vision software, putting AI inference pipelines into production for IDVerse. He is enormously passionate about the developer community, having been a Rust, Go, Python and C++ developer for over a decade. Jack enjoys making complicated topics simple and fun to learn, and heâs dedicated to teaching the world about Mojo ð¥.



================================================================================

URL: https://www.modular.com/blog-all?topic=Company

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?author=Joe+Pamer

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog/semantic-search-with-max-engine

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

March 21, 2024

# Semantic Search with MAX Engine

Ehsan M. Kermani

In the field of natural language processing (NLP), semantic search focuses on understanding the context and intent behind queries, going beyond mere keyword matching to provide more relevant and contextually appropriate results. This approach relies on advanced embedding models to convert text into high-dimensional vectors, capturing the complex semantics of language. In this blog post, we will use Amazon Multilingual Counterfactual Dataset (AMCD) which comprises sentences from Amazon customer reviews annotated for counterfactual detection (CFD) in a binary classification task. Counterfactual statements refer to hypothetical scenarios that have not occurred or are impossible to occur. Such statements are typically recognized as having the structure â If p were true, q would also be true, where both the antecedent (p) and the consequent (q) are understood or presumed to be untrue. For instance, a review stating "If this camera had a better lens, my photos would be perfect" suggests a desired improvement (a better lens) that is currently absent, impacting the outcome (perfect photos).

Our classifier will employ the bge-base-en-v1.5 model within the MAX Engine which has 768 embedding dimensions. The BGE model is distinguished as one of the leading text embedding models on the MTEB leaderboard characterized by its minimal disk size of 416MB and available variants with 768 and 1024 embedding dimensions. Furthermore, we will leverage a vector database to store embeddings generated from the training dataset, simulating real-world conditions for batched inference processing. During inference, we will identify the top 10 most similar reviews (using cosine similarity) and assign probabilities to test queries. Subsequently, we will evaluate the classifier's effectiveness through metrics such as accuracy, F1 score, precision, and recall, applying a 0.5 cutoff threshold. Ultimately, we will contrast the performance of MAX Engine with PyTorch and ONNX runtime across various batch sizes, illustrating thatÂ

- For small batch sizes on CPU, MAX Engine outperforms PyTorch and ONNX runtime by up to 1.6 and 2.8 times, respectively.

- With large batch sizes on CPU, MAX Engine outperforms PyTorch and ONNXÂ runtime by 2 and 1.8 times, respectively.

To install MAX, please check out Get started with MAX Engine. Also have a look at Getting Started with MAX Developer Edition in case you missed it.

The code for this blog post is available in our GitHub repository. The MAX version for this blog is 24.1.1 (0ab415f7).

### Dataset and input tokenizer

Letâs first examine the data in Amazon Multilingual Counterfactual Dataset (AMCD)

The dataframe consists of two columns: sentence (from Amazon customer review) and is_counterfactual (the label) and a total of 4018 samples.

For example:

Next we tokenize all the input sentences in data as follows

With the inputs tokenized, we are now ready to proceed to inference and create sentence embeddings.

### MAX Engine inference

In this blog post, we will utilize the ONNX version of the model, available on HuggingFace. We can obtain use the following command (ensure you have Git LFS installed)

The ONNX model is located at bge-base-en-v1.5/onnx/model.onnx.Â

Below, we create a session object and load the model into maxmodel. We also examine the input and output tensors, noting their names, shapes, and data types:

The model has three input tensors â input_ids, attention_mask, and token_type_ids â and one output tensor, last_hidden_state:

The model's pooling configuration file is as follows which we will use later to accurately obtain our sentence embeddings.

#### Optional: Convert to ONNX using optimum

Another notable option is the conversion of models to the ONNX format using the optimum package which can be done through its command-line-interface (CLI).Â

Note that converting to ONNX offers benefits like framework interoperability across different platforms. For instance, to convert the BAAI/bge-base-en-v1.5 model to ONNX:

#### Sentence embeddings

To enhance efficiency, especially with large datasets, we batch the input sentences before embedding. This approach not only accelerates the processing but also helps manage memory usage more effectively.

Here, in each batch we simply call maxmodel.execute and iterate on the training data until all sentences are embedded.

which outputs

After obtaining the embeddings, they can be utilized for various NLP tasks, such as semantic similarity, clustering, or as input features for machine learning models. In the next section, we will store them in a vector database for semantic search.

### Using a Vector Database

Vector databases excel in managing and querying high-dimensional data, making them ideal for storing embeddings. We chose ChromaDB which is an embedded vector database and is known for its efficiency and straightforward usage, particularly fitting for small to medium-sized applications. ChromaDB stands out due to its fast querying capabilities and lightweight nature.

To start, we create a client and a collection to store our embeddings as follows

#### Search in Vector Database collection

To demonstrate the practical application, we query the database using a test sentence. After tokenizing this sentence and generating its embedding, we search the vector database for the most similar entries, using cosine similarity to identify and return the top 10 most similar items. Cosine similarity is particularly effective for embeddings because it focuses on the orientation of vectors rather than their magnitude. Finally, we assign a probability by normalizing the count of positive is_counterfactual results from the top 10 queries, leveraging cosine similarity.

which outputs

#### Assess test accuracy, F1-score, precision and recall

We evaluate our model on a test dataset using common metrics.

- Accuracy provides a general sense of performance

- F1-score balances precision and recall

- Precision measures the model's exactness and

- Recall assesses its completeness

which outputs

### Comparing MAX Engine performance against PyTorch eager and ONNX runtime

Recall that to efficiently compute all sentence embeddings, we processed them in batches, using a batch size of 128. This batching is particularly important in data intensive scenarios for optimizing resource utilization and processing speed. Consequently, we aim to compare the performance of MAX Engine against PyTorch and ONNX runtime across various batch sizes to understand their respective efficiencies in handling batched data.

To make a compelling comparison between MAX Engine, PyTorch and ONNX runtime, we meticulously selected a range of batch sizes and for better visualization, we divided them up into two categories ofÂ

- smaller batch sizes: 1 up to 32 andÂ

- larger batch sizes: 64 up to 4096

These wide arrays allow us to observe the performance scalability and efficiency of each framework under different load conditions. The runtime for each batch size is measured, offering a clear picture of how each framework handles varying volumes of data. This evaluation is crucial for developers and engineers to make informed decisions about the tools and frameworks best suited for their specific NLP tasks, especially in resource-intensive scenarios such as working with large datasets. For completeness, runtime measurements were done on an AWS c5.12xlarge instance.

Now we use the PyTorch, ONNX model and MAX Engine model individually and plot their performance.

This analysis revealed that for smaller batch sizes (1 up to 32), MAX Engine can be up to 1.6 times faster than PyTorch and is up to 2.8 times faster than ONNX runtime for batch inference.

And for larger batch sizes (64 up to 4096), MAX Engine can be up to 2 and 1.8 times faster than PyTorch and ONNX runtime, respectively. This is showcasing MAX Engine efficiency in handling high-volume data processing tasks.

which shows

### Conclusion

We have illustrated the application of MAX Engine with a pre-trained model for counterfactual binary classification, demonstrating the process of storing embeddings in a vector database suited for inference. Furthermore, our comparison between MAX Engine and PyTorch across various batch sizes has revealed that MAX Engine can achieve up to 1.6 and 2.8 times the speed up against PyTorch and ONNX runtime for varying small batch sizes when running on a CPU, and is 2 and 1.8 times faster against PyTorch and ONNXÂ runtime on large batch sizes, respectively. This efficiency gain highlights MAX Engine's potential to significantly enhance processing speed and resource utilization in large-scale NLP tasks.

Additional resources:

- Get started with downloading MAX

- Download and run MAX examples on GitHubÂ

- Head over to the MAX docs to learn more about MAX Engine APIs and Mojo programming manualÂ

- Join our Discord communityÂ

- Contribute to discussions on the Mojo and MAX GitHub

Report feedback, including issues on our Mojo and MAX GitHub tracker.

Until next time!ð¥

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Developer

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Ehsan M. Kermani

AI DevRel

Ehsan is a Seasoned Machine Learning Engineer with a decade of experience and a rich background in Mathematics and Computer Science. His expertise lies in the development of cutting-edge Machine Learning and Deep Learning systems ranging from Natural Language Processing, Computer Vision, Generative AI and LLMs, Time Series Forecasting and Anomaly Detection while ensuring proper MLOps practices are in-place. Beyond his technical skills, he is very passionate about demystifying complex concepts by creating high-quality and engaging content. His goal is to empower and inspire the developer community through clear, accessible communication and innovative problem-solving. Ehsan lives in Vancouver, Canada.



================================================================================

URL: https://www.modular.com/blog/getting-started-with-max-developer-edition

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

February 29, 2024

# Getting started with MAX Developer Edition

Shashank Prasanna

Today weâre thrilled to announce that MAX Developer Edition is now available in preview for developers worldwide! ð¥³ð. In this developer blog post, we'll take an in-depth look at MAX, its key features and capabilities, and how to use it to deploy your first MAX optimized model. Using code examples weâll illustrate its benefits, cover key concepts, and share additional resources to continue your MAX journey.

Before we get started, head over to the MAX documentation page, and follow the instructions to install MAX Developer Edition preview. If youâre looking for a deeper discussion on why we built MAX, what problems it addresses, read our announcement blog post. If you are a Mojoð¥developer and want to learn more about the future of Mojo and MAX together, we have another blog post for you: MAX is here! What does that mean for Mojoð¥?

Roll up your developer sleeves and letâs jump ð¦right into MAX!Â

### MAX Developer Edition (DE) preview

MAX is an integrated, composable suite of products that simplify your AI infrastructure and gives you everything you need to deploy low-latency, high-throughput inference pipelines into production. MAX meets AI developers where they are. It doesn't require a complete overhaul of your AI pipeline or serving infrastructure; it seamlessly integrates with your existing code, enabling you to benefit immediately and take advantage of new features gradually. There are a few different ways to get started with MAX:

- Try it with your existing models: MAX offers Python and C APIs to replace your current PyTorch, TensorFlow, or ONNX inference calls with MAX Engine. With 3 lines of code you can optimize and execute your models up to 5x faster across a variety of CPU platforms (Intel, AMD, Graviton). Additionally, use MAX Serving as a drop-in replacement for your NVIDIA Triton Inference Server.

- Extend and optimize your AI pipelines: MAX is fully extensible with Mojoð¥ - enabling AI developers to innovate faster and unlock new avenues of performance through custom logic. Mojoâs high performance enables you to accelerate your custom preprocessing, post-processing and data logic with a familiar Pythonic code.Â Â

- Supercharge your advanced AI workloads: MAX offers a new Graph API enabling advanced users to author complete graphs. MAX Graphs are hardware agnostic which means it is easy to get high-performance inference due to whole graph-level optimization on a wide range of hardware, including ones weâre yet to see.

Weâll be releasing Enterprise Edition in the near future, sign up to be notified.Â

### Using the MAX Developer Edition

The first release of MAX: MAX Developer Edition 24.1, comes with everything you need to build and deploy high-performance AI pipelines. Mojo is now bundled with the MAX SDK and weâve simplified the versioning of Mojo to match the MAX version, which follows a YY.MAJOR.MINOR version scheme. Because this is our first release in 2024, that marks this version 24.1. We dive deeper into the rationale behind the decision in a dedicated blog post: MAX is here! What does that mean for Mojoð¥?

MAX Developer Edition 24.1 includes:

- New MAX Engine: A state-of-the-art graph compiler and runtime library that compiles and executes models from TensorFlow, PyTorch, ONNX, and MAX Graphs to deliver high performance, low-latency inference speed on Intel, AMD, ARM CPUs. Reduced precision inference and GPU support are coming soon.

- New MAX Serving: A serving solution for MAX Engine that provides full interoperability with existing AI serving systems (such as Triton) and that seamlessly deploys within existing container infrastructure (such as Kubernetes). You can use pre-built Docker container images or build your own with custom inference pipelines

- Latest release of Mojo: A massive update to the world's first programming language purpose-built for AI. Mojo seamlessly integrates with MAX Engine enabling AI developers to author high-performance inference pipelines with custom pre/post-processing steps, and model architectures using MAX Graphs API. Mojo brings the MAX platform together to deliver unparalleled performance and programmability for any hardware.

Here is how they all fit together. Weâll dive deeper into each component in the next section.

This initial release of the MAX SDK supports Ubuntu 20.04/22.04 LTS on Intel and AMD x86-64 CPUs, and AWS Graviton3 CPU ARM CPUs. We will expand to additional operating systems, hardware, and tool features in upcoming releases. To install MAX SDK head over to the install instructions in the MAX documentation.Â

### MAX Engine

MAX Engine consists of the MAX compiler and runtime library, and Python, C and Mojo APIs to interact with it. It also comes with the max CLI convenience tool that makes it easy to benchmark models and visualize graphs.

To help you get started with MAX, weâve added a number of getting started examples on GitHub, which you can get access by cloning the MAX examples repository:

To run a simple benchmark run:

Running MAX Engine inference on roberta model on an Amazon EC2 c6i.4xlarge instance is 2.94x faster than TensorFlow and 1.74x faster than PyTorch, right out of the box!

To run more examples you can use the convenience console.py script in the examples directory script

To run a simple benchmark run:

To learn how the examples are written or modify it to adapt it for your workflows, head over to the relative paths for each script shown in the console table. You can also use the max CLI tool to run benchmarks on your models and visualize models using the netron app.

### MAX Engine APIs

MAX Engine supports APIs for Python, C and Mojo enabling you to integrate MAX Engine compiler and inference runtime into a variety of applications. The APIs are very simple and straightforward to use. For example, MAX Engine Python API is only 3 lines of code to optimize the model:

And 1 line of code to execute it:

Letâs plug these 4 lines of code into an end-to-end example which downloads a ResNet50 model using Keras, saves it and runs it using MAX Engine, and prints the results. Weâll classify an image of a turtle thatâs already in the MAX examples repository that we cloned earlier. You can find same script on GitHub.

Output:

As you can see, using the MAX Engine API is very straightforward and blends in with your existing workflows. You can similarly use Mojo API for even more flexibility and higher-performance when integrating with authoring pre-/post processing and data loading steps in Mojo. Take a look at the stable diffusion example implemented end-to-end using the MAX Engine and Mojo where the text encode, image decoder and image diffuser models are executed with MAX Engine along with a high-performance tokenizer written in Mojo. You can take this further and optimize end-to-end graphs in MAX Engine by using the MAX Graph API as weâll see in the next section.

### MAX Graph APIs

MAX Graph APIs enable you to define end-to-end models, pre-processing and post-processing steps as part of a graph using predefined operators. With no other dependencies, the entire graph will be executed directly by the MAX Engine for improved runtime performance. MAX Graphs are completely portable and donât need to be rewritten for different hardware.Â

MAX Graphs are lazily-executed and graph operations canât be called eagerly, they have to be compiled using MAX Engine to be executed. MAX examples repository has an example of llama2.ð¥ implementation using MAX Graph APIs. Letâs take a look at what the Graph API looks like for a very basic graph:

In the code above, we first initialize the graph called basic_add with two int32 Tensor (TypeTuple(MOTensor(DType.int32), MOTensor(DType.int32))) and one return arguments of Type Tensor (MOTensor(DType.int32)). The graph contains a single operator called mo.add which we define using the g.op() function. It also takes in the two input Tensors to add and return the result. Visit the Graph API documentation page to learn more.

### MAX Serving

MAX Serving is a deployment solution for MAX Engine that works as a drop-in replacement for your existing server-side inferencing runtime. It doesn't require any changes to your client programs. It implements KServe APIs so you can continue sending inference requests to the cloud with the same APIs you already use.

Weâve created a Docker container that includes MAX Serving (Triton and MAX Engine), which you can either download and run, or build yourself from a Dockerfile. Letâs take a look at how you can easily test your models in a serving environment.Â

Note: Currently, MAX Serving is available only for local development, as part of the MAX Developer Edition. It is not currently licensed for production deployment, but that's coming soon in the Enterprise Edition.

Building on top of our earlier example, letâs prepare our resnet50 model for deployment. Create model-repository directory and copy over the saved model directory to model-repository > resnet50 > 1 > resnet50_saved_model. The directory structure should look like this:

Using the pre-build Docker container image provided you can easily deploy your model with MAX Serving using the following script which includes the model-repository directory name and the specific model we want to deploy. You can find the script below on GitHub.

Once the Docker container is running, you can send inference requests from an HTTP/gRPC client via tritonclient,

Now run the following script to submit inference requests and get results back. You can find this script on GitHub.

Output:

You can see that the results match our example in the MAX Engine section. In this case, the request went over the network to the serving framework, through the MAX Engine runtime, and round-trip back to the client. MAX Serving container in the Developer Edition is a great way to test your end-to-end serving workflows.

### Whatâs new in Mojo SDK v24.1?

#### MAX ð¤ Mojo

With the release of MAX Developer Edition, Mojo is now included as part of the MAX SDK. In preparing the MAX release, we recognized the pivotal role Mojo plays in MAX in authoring high-performance inference pipelines. Consequently, we opted to bundle Mojo within the MAX package for a few reasons.

- Improved User Experience (UX): A single package for MAX and Mojo, eliminating version mismatches or complexities.

- Seamless Integration: Mojo is intricately woven into MAX Engine APIs and maintaining identical versions of MAX and Mojo ensures seamless integration and functionality.

- Streamlined Version Management: By consolidating MAX and Mojo into a unified package, we avoid the need for users to manage multiple versions independently. Both follow a YY.MAJOR.MINOR version scheme. This also simplifies our software delivery process.

This doesnât change the way you use Mojo today or moving forward. Read more about our reasoning behind this change in our blog post: MAX is here! What does that mean for Mojoð¥?

Todayâs release of Mojo SDK v24.1 includes a slew of exciting features in the core language and standard library. Weâll do a dedicated blog post diving deep into a list of new features, changes and bug fixes, in the meantime check out the changelog. Below weâll call out a few exciting additions to the Mojo developer experience.

#### Mojo debugging support is here!Â

The official extension on the Visual Studio Marketplace already supports a number of developer productivity features such as syntax highlighting, diagnostics, fixits, definitions, references, code completion and more. Starting today, debugging support is now officially part of that list!

You can now get interactive debugging experience in Visual Studio Code and via the LLDB command line interface. Mojoâs debugger is able to seamlessly debug hybrid Mojo/C/C++ code in the same debug session, empowering developers even further when working with highly specialized code. Learn more about Mojo Debugging in the documentation.Â

#### New Mojo Playground Experience

We launched Mojo Playground, a hosted set of Mojo-supported Jupyter notebooks, on May 2nd 2023 when we first announced Mojo to the world. Since then, weâve released a downloadable Mojo SDK for Mac and Linux, used by thousands of developers worldwide. Weâre excited to announce that the playground is now more tightly integrated into the documentation experience. Head over to MAX documentation > Mojo > Playground to experience the evolution of Mojo playground.Â

Note: We will be turning down the Mojo Playground (based on Jupyter) on March 20th, 2024. If you have any notebooks you would like to save, please download them with the FileâDownload option we provided in the playground.

### MAX â¥ï¸ Modverse Community

Since our launch of Mojoð¥programming language last year in May 2023, our developer community has grown to more than 170k members worldwide. Weâre thrilled to see all the exciting projects by Mogiciansðªworldwide. And canât wait to see what amazing things the Modverse community will build with MAX DE.

Since releasing Mojo SDK to developers in September 2023, it has been downloaded 55K times, and the Modverse community on Discord has grown to over 21 thousand members from more than 150 countries.Â

Weâre ever grateful for the amazing Mojo champions Aydyn, Lukas, Kent, Mitchell, and Maxim for dedicating their time, and the select group of community members for beta testing MAX DE and helping us iron out those rough edges.Â

We want to express sincere thanks to every member of our community for your support, feedback, enthusiasm, and commitment. ð

### Onward and upward: MAX throttle ð

MAX Engine and Mojo unlock possibilities never seen before in AI and we are thrilled to see what youâll build with MAX! Download MAX Developer Edition today! The entire team at Modular is hard at work continuing to build MAX and add features and platform support youâre looking for. We want to be transparent with our priorities and you can see our upcoming priorities and roadmap hereÂ

Mac and Windows users, we â¥ï¸ you, stay tuned!Â

For enterprise developers, a commercially-licensed Enterprise Edition is in the works. The Enterprise Edition will allow you to seamlessly test and deploy the same MAX components you've downloaded in the Developer Edition into a production environment. If this is you, reach out to us!

Finally, join us on our upcoming Modular Community Livestream: MAX DE Edition on Thursday, March 7th at 11 AM PT! Attend to learn more about MAX DE and get your questions answered directly from Modular engineers.

Weâre just getting started! Here are other ways to learn more and get involved:

- Get started with downloading MAX

- Download and run MAX examples on GitHubÂ

- Head over to the MAX docs to learn more about MAX Engine APIs and Mojo programming manualÂ

- Join our Discord communityÂ

- Contribute to discussions on the Mojo and MAX GitHub

- Report feedback, including issues on our MAX and Mojo GitHub tracker

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Developer

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Shashank Prasanna

AI Developer Advocate

Shashank is an engineer, educator and doodler. He writes and talks about machine learning, specialized machine learning hardware (AI Accelerators) and AI Infrastructure in the cloud. He previously worked at Meta, AWS, NVIDIA, MathWorks (MATLAB) and Oracle in developer relations and marketing, product management, and software development roles and hold an M.S. in electrical engineering.



================================================================================

URL: https://www.modular.com/careers#open-roles

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# Join us in building the future of AI

Watch Video

## We want to enable AI to be used by anyone, anywhere.

Our ambitions are enormous, but working here will feel very familiar. You can change the world without giving up your life

## Why Modular?

Grow with the best

Build with some of the industry's best AI leaders.

Maximize how you work

We will always push the limits to create the best possible environment for our people and teams.

Read how we work.

Build AI for the world

Build a next-generation developer platform, with production quality infrastructure, for the world.

Have fun, live life

Regular team onsites, local meetups & fun, strong team collaboration and more.

## Work and life, balanced.

ð©º

Leading medical, dental & vision packages

ðµ

Strong compensation & equity packages

ð¼

Generous maternity & paternity leave

ð

401K Plan

ð

Work wherever you want

ðï¸

Unlimited Vacation & PTO

ð¥³

Corporate perks & epic team fun

## Current Job Openings

Come and be part of a world-class team that is rebuilding AI for everyone. We welcome applications from all backgrounds and communities.

Engineering

(0)

Text

Location

## What you expect from the interview

Modular has a straight-forward interview process and a large majority of the roles follow the same steps:

Step 01

### Application Review

The first step of the process is to submit your resume. We aim to review all resumes within 2 weeks and we respond to every single application made.

Step 02

### Call with Recruiting

Typically you will first have a short 20-30min call with recruiting to just go over some basic information about you and the company, and to discuss next steps.

Step 03

### Call with Hiring Manager

The Hiring Manager will dig a bit deeper into your background and previous experience to determine mutual fit. This will also be your opportunity to learn more about what the team is working on and what the role entails. In case you have applied for more than one role on different teams, we often schedule you for two separate calls with two separate Hiring Managers.

Step 04

### Team Interviews

There are typically 4 team interviews, usually 45-60 minutes each. These may be scheduled all at once or separately, depending on the role. The focus areas for these team interviews vary by role. For example, for Engineering roles, you can expect coding and system design type interviews; for Product roles, we often talk about the industry and product strategy. For Leadership roles, we may ask for a presentation.We believe that team culture is a critical part of a great company, so every candidate also has a Culture interview that focuses on communication skills, team work, and so on. Â We will let you know what to expect in each interview when we send you a schedule confirmation.

Step 05

### Founder Interviews

We aim to put all finalists in touch with at least one of our co-founders before a final decision. This would allow you as a candidate to speak directly to Chris and/or Tim, and get answers to any final important questions you may have.

Step 06

### Offer

If we make you an offer, we share an extensive offer deck for you and schedule a walk-through call to go over the information in detail and again answer any final questions you may have. We also aim to give you enough time to make a decision after that.

## Why should you be excited aboutÂ Modular?

âI picked modular because of the people and what they believe in and what they want to do. I want to be a part of something that makes a difference for the better.â

Paige Bedwell

Program Manager

âThe team is incredible. They're very smart. They're experts in their field, and we're solving the hardest computational problems in the world.â

Abdul Dakkak

AI Compiler Engineer

âThe best part of working here is a strong commitment to culture. I work on the most challenging problems, have the freedom to do it anywhere, and have a huge impact on AI.â

Eric Johnson

Product Lead

## Frequently asked questions

Are interviews conducted in-person?

Typically of our interviews are conducted over Zoom video. For coding interviews, we use Coderpad and you will receive a link from your interviewer. In some cases we may fly you in to meet some of our leaders for the last interview.

What do you look for in a candidate?

There is no âone size fits allâ answer here however Modulers are passionate about their work, intellectually curious, humble, and ready to roll up their sleeves. We like to have discussions in interviews and expect you to ask clarifying questions and talk us through your thought process â sometimes there is no right or wrong answer, we just want to understand how you think and work through challenging problems. We also look for strong alignment with Modularâs Culture and values.

I donât meet all of the requirements for a role, can I still be considered?

We would love to receive your application even if it doesnât meet all criteria! We review applications with all roles and opportunities in mind. Sometimes we may reach out to you to offer or suggest another role or team that might be a better fit for you, or we offer to keep your application on file and reach out a few months down the line once more suitable opportunities open up.

How long does the interview process take?

On average it takes candidates about 4 weeks to go through the full interview process however if you require a decision by a certain date, please speak to your recruiter.

Do you offer internships?

We want to ensure that our interns have an outstanding experience, which includes a well-defined project and goals, meaningful and impactful work, and strong and attentive mentorship. As we are still a small team, we are not ready to offer internships at this time but we will be sure to update the website as soon as it changes!

Do you provide visa assistance?

Yes, we offer premium processing for our H1-B transfer candidates. We can also sponsor H1-B or offer other relevant visa application assistance to eligible candidates.

Do you provide Green Card assistance?

Yes, we also offer Green Card application assistance to eligible candidates, just speak to your recruiter for more details.

Do you provide relocation assistance?

As we are a remote-first company, we generally do not relocate candidates except new graduates who are moving away from campus. If Modular asks you to relocate, we will also provide you with a relocation stipend.

Are there any opportunities for professional development?

Modular is growing rapidly so there are a variety of growth opportunities available â whether thatâs moving into a TL role, management, or a lateral transfer to a different team. We provide our managers with training to walk you through your career planning, and we have a well-defined but lightweight performance review process that ensures your career goals are discussed at a regular cadence.

I applied some time ago but havenât heard back?

We aim to respond to every single candidate, and if you have applied to more than one role, we process all of your applications separately. Sometimes these emails end up in spam, so please check your spam folder. If you havenât heard from us, you can contact your recruiter or careers@modular.com to follow up.

Will you provide feedback if I am rejected?

We sincerely appreciate the time it takes to interview, so we always provide some feedback as to why your application did not work out if we have spoken to you. If you have not heard from us within 2 weeks after your last call, please follow up with your recruiter.

What benefits do you offer?

Modular offers some world-class benefits for all employees. Our benefit package changes over time, and may depend on your specific location, but US-based employees can expect benefits that include:

- A variety of fantastic health benefits (health, dental, vision insurance; life insurance etc) are available.

- A 401k plan with up to 5% match.

- Access to Passport Corporate Discount Program, LinkedIn Learning, and free tax advice on Carta.

- Generous work-from-home stipend of $1500 to help you improve your home office.

- Unlimited paid time off and flexible work hours.

Who we are

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/career-post?4329795005&gh_jid=4329795005

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

CAREERS

/



# 



- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/team/zac-bowling

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/team/swetha-muniraju

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?author=Zac+Bowling

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?author=Swetha+Muniraju

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog/the-case-for-a-next-generation-ai-developer-platform

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

June 30, 2022

# The Case for a Next-Generation AI Developer Platform

Chris Lattner

Tim Davis

Eric Johnson

### AI promised to profoundly change the world, so why hasnât it?

From healthcare to manufacturing, finance, climate, communication, and travel, to how we live and work. AI can help solve any problem that can be represented by data, assuming the right algorithms and enough computational resources.

Today we have all the ingredients to realize the benefits of AI. More data is being generated through billions of phones and IoT devices than ever before in human history, and we have near unlimited storage and compute access in the cloud. Meanwhile AI hardware has made enormous improvements in the cost and performance of raw compute, and the rate of AI research papers published yearly is now vastly superlinear.

Despite the promise and initial successes of AI, it has yet to impactfully penetrate our lives â we donât have widespread autonomous cars and buses, preventive medicine still relies on doctorâs visits and stethoscopes, drug discovery remains a speculative investment game, global food security is unsolved, farming still relies largely on hard human labor, and real robotic automation at home is still for the Jetsons.

Instead, we are getting more targeted advertising, better social media recommendations, cute AR glasses, predictive credit scoring, and âsmartâ speakers. These successes may be ubiquitous, but they are also far from realizing the full potential we see coming out of ML research on a daily basis.

AI is a triumvirate of data, algorithms, and hardware, and we are rich in all three - so why isnât it getting fully utilized? The reason may not be apparent to many, because the truth is deeper than the stream of AI research updates coming from the worldâs largest tech companies (MAGMA aka Big Tech) and the press.

### If AI is so important, why is its software so bad?

AI software is fundamentally broken because it was designed to be used by the full stack researchers, engineers and architects that built the technology in the first place â it was never built as a product. This software was built by Big Tech companies to solve their own problems, and now the rest of the world runs on âtrickle down infrastructureâ. The consequence of this is that only the largest and most business-impactful uses of AI get built and deployed in practice, and even then, only if your needs align well with the in-house needs of Big Tech companies.

Why is this? AI software today is monolithic and research quality, fractured into technology silos that follow the organization chart of the Big Tech companies who built it. It was created by researchers to do research, and the quick evolution of AI has left no time to pause and build it right. Instead we've just piled on more and more layers of complexity over time, resulting in an industry that is struggling to maintain and scale fragmented custom tool chains that differ between research and production, training and deployment, server and edge. AI systems have become a vast sea of incompatible technologies that only the largest and most sophisticated companies can brute force to achieve their goals.

### Isnât Big Tech going to solve the worldâs AI problem for us?

The successes at deploying AI come from teams who amassed talented armies of AI researchers and developers. Big Tech uses their vast compute and financial resources to further the priorities of their products and core business, including their own clouds, phones, social networks, and AI hardware. Despite their incredible contributions to the field, it doesnât make business sense for Big Tech to lift AI for the world (spanning all hardware, clouds, and ML frameworks), and the rest of the world cannot expect them to do so. This unfortunate truth imposes an industry-limiting ceiling on the rest of the worldâs ability to use this technology to fight problems outside the Big Tech focus areas, including some of the most significant socioeconomic and environmental problems the world faces. That's not a future we choose to accept.

These companies make incredible contributions to the progression of AI, but for AI to reach its full potential, we need an independent company that doesnât prioritize its own hardware, its own cloud infrastructure, its own mobile phones, or its own research. We need a neutral company that does what is in the best interest of users and companies around the world. We need to incorporate the learnings from the rapid growth in AI software into a next-generation of technology that is designed for usability and generality, for all organizations in all problem domains.

Today, the most pressing problem facing companies who arenât Big Tech is the ability to productionize AI within performance, cost, time, and talent bounds. Their opportunity cost means innovations not making it to market, inferior product experiences, and ultimately a negative impact on their bottom line. But for society as a whole, this means a longer timeline to solving some of the worldâs most critical problems with AI. We donât have the time to wait for trickle-down AI software from the worldâs largest companies. AI can change the world, but only once fragmentation has been repaired and the global AI developer community can take high quality infrastructure for granted, rather than fighting to get the infrastructure to work.

### If not Big Tech, who will solve this for the world?

Modular is building a next-generation AI developer platform focused on usability, velocity, and flexibility. Our platform unifies popular AI framework front-ends via common interfaces, and enhances access and portability to a wide range of hardware backends and cloud environments. We are rebuilding core developer workflow tooling to be more expressive, usable, debuggable, reliable, performant, and scalable. Our tools can be easily adopted into existing workflows without rearchitecting or rewriting your code, meeting you where you are today and enabling you to realize improved productivity and performance improvements at lower cost. We will accelerate AI value capture and lower time to market for our customers.

But weâre not content with the status quo. We believe that the full potential of AI will be realized when it can permeate applications in a fine-grained way - AI shouldnât be something you need to shape your application around. Our platform is built of layered, composable infrastructure components that can be remixed and extended to enable a wide-variety of use cases, while also allowing domain experts to innovate without needing to know how the whole system works. We have seen first-hand how a layered and composable approach can unlock new use cases that havenât been imagined.

To truly fix AI infrastructure, we are both solving âhard techâ problems (like compilers for exotic heterogeneous compute technologies) as well as building seamless end-to-end developer workflows. To achieve this, our team is made up of the software architects, engineers, and product leaders who have already built world leading developer platforms and the current generation of AI frameworks. We have listened to thousands of users and developers across many years, felt the pain ourselves, and are deeply focused on building products users love.

### What does this new world look like?

Our success means global developers will be empowered by access to AI software that is truly usable, portable, and scalable. A world where developers without unlimited budgets or access to top talent can be just as efficient as the worldâs largest technology giants, where the efficiency and total cost of ownership of AI hardware are optimized, where organizations can easily plug in custom ASICs for their use cases, where deploying to the edge is as easy as deploying to a server, where organizations can use any AI framework that best suits their needs, and where AI programs seamlessly scale across hardware so that deploying the latest AI research into production âjust worksâ.

We see an AI industry that isnât dependent on the timeline and capabilities that the top Big Tech companies decide they need. We see an industry that moves even faster and is less fragmented. We see an industry where innovation thrives at all levels of the stack, allowing developers to focus on bringing new innovations to market in their domains of expertise, and to build a better future for all of us. We see an industry with high velocity, lifting us from the âEra of AI Researchâ to the âEra of Production AIâ. Thatâs a world worth fighting for and a world we can look back on and be proud of.Â

Thatâs the world Modular is creating, and the time to do it is now. A better future starts today, and we are assembling the best talent on the planet to do it. We are proud to have raised $30M from GV, Greylock, Factory, SV Angel and notable angels who are backing us - investors who understand AI infrastructure deeper than anyone and recognize its importance to the world.

Join us in building a Modular AI platform for the World.

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Company

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Chris Lattner

Co-Founder & CEO

Distinguished Leader who founded and scaled critical infrastructure including LLVM, Clang, MLIR, Cloud TPUs and the Swift programming language. Chris built AI and core systems at multiple world leading technology companies including Apple, Google, SiFive and Tesla.

clattner@modular.com

Tim Davis

Co-Founder & President

Repeat Entrepreneur and Product Leader. Tim helped build, found and scale large parts of Google's AI infrastructure at Google Brain and Core Systems from APIs (TensorFlow), Compilers (XLA & MLIR) and runtimes for server (CPU/GPU/TPU) and TF Lite (Mobile/Micro/Web), Android ML & NNAPI, large model infrastructure & OSS for billions of users and devices. Loves running, building and scaling products to help people, and the world.

tim@modular.com

Eric Johnson

Product Lead

Product leader who has built and scaled AI applications and infrastructure. Eric led the TensorFlow API, Compiler, and Runtime teams at Google Brain and Core Systems, including the founding of TFRT and the productionization of JAX. He holds an MBA from Wharton and Computer Science MS from Penn and loves soccer, fitness, and the great outdoors.

eric@modular.com

================================================================================

URL: https://www.modular.com/blog/the-future-of-ai-depends-on-modularity

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

April 26, 2022

# The future of AI depends on Modularity

Chris Lattner

Tim Davis

Platforms like TensorFlow, PyTorch, and CUDA do not focus on modularity - there, we said it! They are sprawling technologies with thousands of evolving interdependent pieces that have grown organically into complicated structures over time. AI software developers must deal with this sprawl while deploying workloads to server, mobile devices, microcontrollers, and web browsers using multiple hardware platforms and accelerators.

The existing monolithic systems mentioned above are not easily extensible or generalizable outside of their initial domain target, which has forced hardware makers to build their own technology stacks. The consequence is a hugely fragmented AI deployment industry with dozens of toolchains that carry different tradeoffs and limitations. More importantly, these design patterns have also slowed the pace of innovation by being less usable, less portable, and harder to scale.

We have seen and contributed to AI projects that touch the human and natural world in profound ways - whether to save the Great Barrier Reef, help people find their rhythm, or teaching people to dance. Unfortunately weâve also seen that the technical complexity of building and deploying these applications is still too high. Deploying AI remains the domain of full stack experts, and cutting edge applications are only accessible to people at the biggest tech companies that built the ML technologies themselves.

Imagine a world where ML research truly flows rapidly and effectively into production from a large global community. One where these breakthroughs are more accessible to everyone, allowing product innovators to drastically improve our daily lives and be freed from the chains of software and hardware complexity. In this world, AI would be more usable, more portable, more accessible, more scalable, and would enable a much bigger community to massively impact our health, the environment, recreation, finance, manufacturing, and commerce, among many other industries.

We donât think so. The software industry goes through cycles, and weâve seen and solved problems like this before.

## Flash back to software in the 1990âs

The software world in the 1990âs had fragmentation problems similar to what we see today with AI. At the time, C and C++ had established communities, but were fragmented across dozens of proprietary compilers. Each had vendor extensions, rough edges, strange corner cases, and incomplete implementations. It was so difficult to build cross-platform software that tools sprung up to help developers cope with the fragmentation, making it easier to install, build, and configure software (e.g., autoconf).

We were saved by the rise of GCC, which became massively successful throughout the 90âs by virtue of its cross-platform support, good performance, and stability, and by being free. GCCâs success drove a wave of consolidation in the tools industry, and the resulting defragmentation enabled a wave of new innovations by making its capabilities the de-facto standard. It catalyzed a revolution both in software (directly contributing to the rapid rise of OSS communities like Linux) and hardware (enabling innovation in instruction set architectures and new business models) by freeing the former from fragmented C/C++ implementations, and the latter from having to chase rapidly evolving C/C++ language standards.

While the computing world owes a debt of gratitude to GCC, it had some architectural challenges. GCC followed the classical parser, optimizer, code generator architecture used by all modern compilers, but it was intentionally designed as a monolithic batch compiler system, and GCCâs leadership resisted attempts to improve modularity and design, a continued source of friction in the community.

## The rise of Modularity

It took time for the world to notice, but the year 2000 was a seminal moment in compilers and programming languages: it was the start of the LLVM project.

Twenty-two years later, LLVM/Clang powers much of the world's computation - it is now widely adopted across iOS, Android, Google, Meta, and many other companies and systems. However, one might wonder how this happened: LLVM/Clang uses the standard âparser, optimizer, and code generatorâ approach (similar to its predecessors), it doesnât have breakthrough code generation algorithms, and other systems eventually followed the early innovations like âwhole program optimizationâ. LLVM succeeded despite merely being âon parâ with existing compilers on traditional C/C++ workloads.

The innovative aspect of LLVM is its software architecture: LLVM is designed as a collection of composable libraries. These libraries have defined interfaces that allow them to be composed and extended in innovative ways. They may be built into large-scale software projects or remixed for very small applications (e.g., domain-specific Just-In-Time compilers). Modularity and clear interfaces encourage testability, which contributes to higher quality implementations. Modularity and separation of concerns allow domain experts to work on large-scale projects without knowing how the whole system works.

In a 2011 retrospective, modularity was cited as enabling the LLVM community to scale, leading to new developer tooling like clang-format, and unlocking innovative programming languages (Rust, Julia, Swift and more). These were technically possible before, but they actually happened because of the usability and hackability of LLVM. Modularity design enabled next-generation Just-In-Time accelerator programming models like OpenCL and CUDA. This drove the next wave of consolidation in compiler technology, which is why LLVM now underlies most CPU, GPU, and AI systems today.

The most exciting contribution of LLVM is that it unlocked new use-cases that werenât planned from the beginning. AI wasnât part of its original design, nor was it designed for Snowflake to use in their database query optimizer. The dual to this observation is that many of these use cases would never have happened without LLVM (or something like it) available: a database team isnât likely to build a JIT compiler for query optimization if they have to start by creating an X86 code generator from scratch.

## AI Infrastructure in 2022

Today we can see great strides in the AI industry. For example, we have data scientists around the world training models on 100+ PetaFLOP supercomputers from a Jupyter notebook. That said, end-to-end deployment of those models is still far from being âdemocratized.â The tools used to deploy AI models today are strikingly similar to compilers and tools in the 1990âs and 2000âs. We see severe fragmentation across these systems, with a wide variety of hardware, each having bespoke tools.

The worldâs biggest tech companies have built multiple in-house toolchains specific to different hardware products over the years, and these are often incompatible and share little code. How many flaky converters and translators does one industry really need?

Many in the industry believe that these issues are due to the inherent nature of AI, but we know this is because AI infrastructure is still in its infancy. AI is already having such an incredible impact on the world, but we have to wonder: How much bigger could the impact of ML be if we had the opportunity to rebuild it the right way?

## The world deserves Modular AI

We have learned so much from the development of AI infrastructure and tools over the last ten years. The industry has made great strides, and much that was once research is now well understood. It is now time to incorporate the lessons learned into a single layered and composable system that integrates the best-known technologies from across the industry.

It must not require an expensive rewrite, re-architecting, or re-basing of user code. It must be natively multi-framework, multi-cloud, and multi-hardware. It needs to combine the best performance and efficiency with the best usability. This is the only way to reduce fragmentation and unlock the next generation of hardware, data, and algorithmic innovations.

This is a huge and important task. Achieving this requires extraordinary collaboration across a team of architects, engineers, and leaders who built many of the existing systems, and who are driving the state of the art forward. It requires focus, discipline, and a commitment to technical excellence - a value system that incentivizes building things the right way. It requires the strength to say ânot yetâ to many interesting projects, allowing us to ensure we get the fundamentals right.

Our goal is to create a world where AI is more useful, more accessible, more portable, more scalable and enables developers everywhere to positively impact the world in untold ways. In this world, more time is spent on using AI to solve problems, rather than wrestling with a fragmented set of low-quality tools and infrastructure. This is the world we seek to create.

We are building the future of AI and our vision is to enable AI to be used by anyone, anywhere.

Welcome to Modular.

â

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Company

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Chris Lattner

Co-Founder & CEO

Distinguished Leader who founded and scaled critical infrastructure including LLVM, Clang, MLIR, Cloud TPUs and the Swift programming language. Chris built AI and core systems at multiple world leading technology companies including Apple, Google, SiFive and Tesla.

clattner@modular.com

Tim Davis

Co-Founder & President

Repeat Entrepreneur and Product Leader. Tim helped build, found and scale large parts of Google's AI infrastructure at Google Brain and Core Systems from APIs (TensorFlow), Compilers (XLA & MLIR) and runtimes for server (CPU/GPU/TPU) and TF Lite (Mobile/Micro/Web), Android ML & NNAPI, large model infrastructure & OSS for billions of users and devices. Loves running, building and scaling products to help people, and the world.

tim@modular.com

================================================================================

URL: https://www.modular.com/blog/increasing-development-velocity-of-giant-ai-models-part-2

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

November 10, 2022

# Part 2: Increasing development velocity of giant AI models

Abdul Dakkak

Eric Johnson

This is part two of a two-part series on how we are fixing the compilation stack for developing with large models. Read part one here. Subscribe to our newsletter to get future posts delivered to your inbox!

### A quick recap

In our last post, we highlighted multiple requirements we needed to satisfy large model development. A brief recap of these is below:

- Only allocating when necessary:Â It is more efficient to memory map large data (like weights) from disk, allowing the operating system to page in data on demand.â

- No hashing or uniquing: Checking equality of 2 gigabytes isnât something we should ever be doing; weights should be identified by name instead of being implicitly uniqued by content.â

- Enabling in-place mutation: If there is only one user of the data, we should be able to quantize, transform and manipulate data in place instead of making a copy of it first.â

- Destruction: The data we are working with is huge, and we need to deallocate it when the last reference to the data is destroyed.â

- Fast serialization: Whether JITing, searching optimization parameters, or just iterating locally, we cache IR for many reasons, and it should be fast.

### Fixing the weight attributes

The first four requirements address one fundamental problem with how we've been using MLIR: weights are constant data, but shouldn't be managed like other MLIR attributes. Until now, we've been trying to place a square peg into a round hole, creating a lot of wasted space that's costing us development velocity (and, therefore, money for users of the tools).

We decided we needed to manage this weight data differently than other types of attributes. This prompted our first fundamental extension to MLIR, "Resources," a mechanism to separate data from its references within the computation.

Each blob of serialized MLIR may now contain additional sections, known as âresourceâ sections. These sections either include âdialectâ resources (a dialect is essentially a namespace-like abstraction used when extending MLIR) or âexternalâ resources (for toolchain-specific data). The data within these sections is represented using a simple key-value pairing, creating a json-like structure, like so:

/// Here we have some MLIR operations.
module {
  func.func @foo() {
    // Cool stuff here ...
  }
}

/// Here we have an `external_resources` section. The resource section's syntax is designed to be unique as to not conflict with other MLIR syntax (which is user extensible!).
{-#
  external_resources: {
    mlir_reproducer: {
      pipeline: "func.func(cse,canonicalize),inline",
      disable_threading: true
    }
  }
#-}

The above example shows how weâve adapted MLIR to use resources for reproducers. An MLIR reproducer is effectively a configuration containing execution information, such as what transformation pipeline to execute, and is used to reproduce a failure or crash. Before resources, we represented this information using a comment at the top of an MLIR file. Instead, using resources, we have now incorporated it as a first-class piece of information.

To store weights we can now use the resource section to hold the big data blob that used to be unique and immortalized. In the IR, we shift to using light-weight references for attributes instead of the underlying data:

Encoding resources this way also brings some secondary benefits:

- Printing IR for debugging is less error-prone, leading to a better development experience: Resources are specialized sections; we donât have to worry about accidentally dumping 4 gigabytes to the screen while debugging something.â

- We can soundly process the IR without the data present: With the IR only holding references to the data and not the data itself, we can omit the underlying resource data if desired. For example, this greatly simplifies reproducers that donât need the big weight data (consider sending a colleague a 20-megabyte file instead of a 1.2-gigabyte file).

By introducing resources as a new concept, weâve finally been able to build a clean separation between program and data. Now we never pass our weight data directly to an attribute. Instead, we pass a weak reference to the attribute and pass the data to a specialized manager. With this, we now have much more control over when and how weights are allocated, mutated, and destroyed.

### A new binary encoding for MLIR

With a better representation of our weights, the only thing we needed now was a more efficient method of storing these weights when serializing our MLIR representation.

Until this point, MLIR only had a textual serialization format, which used ASCII hex strings for its weight representation. However, our end goal was to have our local development flow be as fast as possible. To do that, it became clear that we needed to remove text and add a proper binary format to MLIR.

Binary formats require a lot of consideration, especially as they often form the basis for stability in compilers. For MLIR, extra layers of trickery are needed given that we need to be efficient for our use cases (which can be different depending on who you ask), we want to be fast, and because MLIR/LLVM cannot add dependencies on third party encoding libraries.

One nice aspect of MLIR, though, is that its generality makes it nearly trivial to encode. All operations in MLIR have the same structure, so every operation uses the same encoding. Most of the complexity is making sure that our few core concepts are compact and very efficient. Given these constraints, we decided to use a custom encoding.

### What is the user impact?

In the end, adding resources and a binary encoding to MLIR has made our toolchain and development workflow significantly faster and reduced our memory usage substantially - making our performance and velocity incredible. Itâs also made everything about MLIR better â more on that later.

To validate this, we tested our changes across models of various sizes, measuring the speed of a real-life lowering and optimization pipeline in our MLIR-based graph compiler (from a TensorFlow serialized model to the input format of our runtime) and the memory used during that process.

Speed: Compilation Workflow

MLIR is now significantly faster. Going from a serialized TensorFlow model (from a checkout of TensorFlow 2.10) to our runtime input format, a process that involves many transformations of the underlying program representation, was ~1.8-2x faster in terms of wall clock time than before, with speed scaling consistently across the various model sizes.

Diving a bit deeper, the TF serialized model processing is now basically instant â all our time is spent writing the big-weight data to disk when generating the MLIR. In fact, the actual time spent in our code is about 10x faster than before. Most of the time is now bounded by the speed at which the SSD writes >1 gigabyte of data to disk.

For ML developers using our tools this means faster model compilation, thereby improving productivity and iteration time. This has benefits for production environments as well when loading (and compiling) models. For example, when dynamically loading and unloading models based on incoming traffic â e.g., use cases with many of personalized/fine-tuned user models.

Speed: Serialization

Also faster is serialization due to the introduction of a binary encoding. Interacting with MLIR via external tools depends on the reading and writing of serialized MLIR â whether for introspection, caching, reproducer generation, etc. Again, we tested serialization performance across various model sizes and saw a significant speed-up, peak performance being SSD bound. More specifically, reading textual data for larger models took ~5 seconds compared to <10ms for reading binary. And writing was > ~5x faster for binary than textual formats.

For Modular, this enables us to develop infrastructure and tooling around MLIR that would otherwise be prohibitively slow or expensive. For example, this would allow us to provide an efficient debugger that relies on caching model representations throughout the compilation workflow, improving the underlying compiler performance, and much much more.

Memory Usage

Finally, the mmap capabilities of our binary serialization and the separation of IR and data via resources have also significantly reduced memory consumption. Across all model sizes, we are using less memory during the compilation process. Where before we had to allocate the relative size of the weights in a model, we no longer have to allocate at all for the weights, meaning we save significant memory every time we compile.

### Building for everyone

At Modular, we intend to lift the AI ecosystem for everyone, not just ourselves. The improvements weâve discussed, both the new resource representation and the binary encoding, have been contributed upstream to the LLVM/MLIR repository. And while we were inspired to make these improvements to MLIR to solve our customerâs problems and improve our internal infrastructure, the impact of these changes isnât limited to just our use cases â it will enhance other products using MLIR as a foundational technology. For example, because of this work, the MLIR community is now discussing MLIR stability guarantees.

Ultimately though, the value of these contributions, and the evolution of these foundational technologies, flow directly into the products we build for our customers. These contributions represent a small look into the vast number of core improvements we are making. Whether working with large models or deploying on-device, Modular is building and is actively working to make all of AI infrastructure significantly more performant and easier to use than anything else in existence today.

Lastly, we are so excited about the future of AI, the LLVM community, MLIR and our contributions from the earliest days - that we made the decision to become a Platinum Sponsor of LLVM to continue to support and grow the LLVM community for many years to come.

If you are an experienced Â developer in AI infrastructure or a MLIR practitioner, you should definitely consider joining Modular. If you like AI and enjoy using great tools, please subscribe to our newsletter.

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Engineering

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Abdul Dakkak

AI Compiler Engineer

Expert in machine learning, compilers, programming languages, and accelerated computing. Before Modular, Abdul led the development of AI compilers for GPUs at Microsoft Research and the Mathematica Compiler at Wolfram Research. Abdul has developed open-source tools for accelerating real-world applications to optimize their performance across the hardware and software stack.



Eric Johnson

Product Lead

Product leader who has built and scaled AI applications and infrastructure. Eric led the TensorFlow API, Compiler, and Runtime teams at Google Brain and Core Systems, including the founding of TFRT and the productionization of JAX. He holds an MBA from Wharton and Computer Science MS from Penn and loves soccer, fitness, and the great outdoors.

eric@modular.com

================================================================================

URL: https://www.modular.com/blog/if-ai-serving-tech-cant-solve-todays-problems-how-do-we-scale-into-the-future

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

December 8, 2022

# If AI serving tech canât solve todayâs problems, how do we scale into the future?

Eric Johnson

Tim Davis

## The AIÂ Serving Substrate

The technological progress that has been made in AI over the last ten years is breathtaking â from AlexNet in 2012 to the recent release of ChatGPT, which has taken large foundational models and conversational AI to another level. These incredible research innovations have shown the immense potential of AI to impact everything from healthcare to manufacturing, finance, climate, communication, and travel, to how we interact with the world around us.

AI can help solve any problem represented by data, assuming suitable algorithms and enough computational resources. However, what often goes unsaid through the myriad of excitement and press around these research innovations is the challenge of practically deploying them. To realize the full potential to improve human lives, they need to permeate through the applications we use daily â they canât live solely in the deep pockets of AI research institutes and Big Tech. In this post, we discuss how deficiencies in existing serving technologies make the deployment of AI models to cloud server environments challenging.

Today's real-world AI applications require a production team to build, maintain and evolve their own AI Serving Substrate.

These substrates typically include machine learning frameworks like TensorFlow, PyTorch, ONNX Runtime and TensorRT, AI serving frameworks like TensorFlow Serving, TorchServe, or Nvidiaâs Triton Inference Server, and containerization and orchestration technologies like Docker and Kubernetes. In doing so, they strive to support user demand and meet or improve on requirements for cost, throughput, latency, and model predictive or generated content quality â all while avoiding hardware lock-in and maintaining cloud optionality.

Achieving these goals is difficult because the current generation of serving substrates are usually custom in-house designs assembled with duct tape from many uncooperative components. This negatively impacts deployment velocity when new kinds of models need to be deployed, leads to reliability problems scaling these complicated ad-hoc systems, and prevents the use of the latest features needed by the most advanced models. As a result, these systems get replaced every few years as requirements change and model architectures evolve.

At Modular, we have productized huge AI workloads and delivered them to billions of users, and we have replaced numerous serving substrates along the way. Letâs look at some of the problems that need to be addressed to solve this once and for all.

## What does a modern AI Cloud application look like?

Modern cloud applications are complex distributed systems orchestrating data-flows across several independent micro-services and components â sometimes spanning both cloud and edge. These distributed applications evolved to need a serving substrate that is responsible for scaling and managing them.

Letâs look at a typical application stack, like automatic speech recognition (ASR) in the cloud, and explore some of the root causes of the problems we have faced.

## The many challenges in production AI serving

Modern AI system have evolved to include many problems that go beyond a simple serving binary.

### The complexity of integrating multiple AI frameworks

A key source of complexity stems from the challenges serving multiple machine learning frameworks (TensorFlow, PyTorch, JAX, ONNX). Production teams are often asked to provide a unified system in order to be agile and responsive to the needs of research teams. The typical solution is to layer on top of multiple frameworks, and use a âlowest-common-denominatorâ wrapper for the underlying runtimes. Â Unfortunately, this typically prevents the use of the most powerful and differentiating features that those frameworks provide.

Another problem is that implementation details such as execution environment (remote vs. local) and communication protocol (gRPC vs. HTTP transport) are leaked to the application developer. As a result, developers typically need to directly manage crucial functional and performance aspects such as fault-tolerance and load-balancing instead of the serving API abstracting these implementation details away.

This is all made worse by the monolithic nature of machine learning frameworks, which are difficult to integrate, have large and complicated dependencies, and think they should own all the resources on a given machine.

### Overly simplified systems have hidden ceilings

Many AI serving substrates provide a simplified API for orchestrating the many components in the system, but are often very limited and can be very slow. Â The challenge occurs when your application becomes successful and you need to start scaling it. These stacks frequently run into reliability problems, fail to scale to larger deployments, do not deliver the latency and throughput requirements needed by the application, and cannot integrate with more advanced use-cases described below.

The appeal of a simple stack quickly loses its charm when you find you need to rewrite your stack to a much more complicated (but also more powerful) substrate.

### Challenges multiplexing applications onto shared resources

At scale, production cloud environments host multiple applications that each have their own AI models. Different models have varying compute and memory requirements, different traffic patterns, and latency and throughput needs - depending on the context of the application. Further, models typically have multiple versioned variants in production for A/B testing new algorithms and to support safe and incremental model update rollouts without disrupting production traffic. These factors combine to create complexity for cloud application developers and their production release and management operations.

These challenges are inherent to our domain, but existing systems do little to help - they push the pain onto DevOps and MLOps teams which drives the need for bespoke serving substrates. These substrates must manage model storage and caching, retrieve model features, load-balance and route model inference requests, proactively auto-scale serving capacity across cloud regions, scale with model and data parallelism, implement monitoring and logging, and respond to dynamically changing traffic volumes. Production teams often have amazing engineers who can tackle and solve these problems, but doing so is often not the highest priority of the team or the best ROI for their time.

### Large AI models create new challenges for scale & reliability

Giant AI models have been growing at an astonishing rate in terms of their size and prevalence across domains (NLP, Vision, Speech, Multimodal AI tasks, etc). While large transformers are wildly popular, other model architectures like Mixture-of-Experts (MoE) and Recommender models (which have a large number of sparse features and embeddings) can also have 100s of billions of parameters. Individual commodity cloud machines lack the memory and float point compute capacity to run these models by themselves.

Distributing these models across multiple machines can help, but the existing substrates are typically bespoke implementations that only work for select model architectures and hardware targets. One small deviation from the supported models and execution environments can require significant rewrites or switching to a new serving substrate. This is a huge problem for organizations that favor fast iteration and high-velocity research.

The reliability of large models is another under-recognized concern, given the distributed serving systems are typically based on frameworks like Message Passing Interface (MPI). MPI and similar systems are designed for reliable High Performance Computing environments, and lack fault tolerance to software, network, and machine failures â this donât work well on commodity cloud environments where such failures are common. Furthermore, they donât support the elasticity needs (scaling the number of workers) that one would expect in a typical scalable cloud environment.

### Challenges managing cost and project-level spend when delivering value

The performance of an AI application in production has far more to it than meeting a specific QPS number, throughput or latency threshold. It must also be manageable as the design changes and the workloads evolve, and costs needs to be tied back to the operations teams responsible for a workload. Most of the challenges come from poor integration of the components in the serving substrate, and gaps in system level performance observability.

Todayâs serving substrates emit some basic metrics like request volume, latency, and device compute utilization, but fail to provide an integrated resource utilization view that covers fixed and variable memory usage, and utilization of key I/O data-paths (Network, PCIe, Disks, etc.). They also don't provide an integrated diagnostic view of end-to-end inference performance to enable pinpointing bottlenecks and unlocking cost and performance optimizations. Finally, the top-line metrics of actual occupancy and utilization rates of allocated compute resources and provisioned compute capacity are not tracked even though they are the dominant factor in operational costs.

It is difficult to see how to really address this while the fundamental components of the substrate are disaggregated and so uncooperative with each other.

## Unlock AI serving for the future

Developers need to be able to integrate AI into performance-sensitive production applications with zero friction and serve AI reliably and cost-effectively at scale. Weâve seen the power of large models, but that can only benefit the world if they can be deployed cost-effectively and reliably â and theyâll keep getting bigger and more complicated. An AI serving substrate that addresses these pain points will dramatically improve the value-to-cost ratio for AI adoption in cloud applications.

Modular is tackling the hardest problems in AI infrastructure because we believe solving these will unlock the power of AI in ways that no one else can. We encourage you to apply for our open roles if you are passionate about these problems and driving the next wave of innovation in AI.

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Engineering

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Eric Johnson

Product Lead

Product leader who has built and scaled AI applications and infrastructure. Eric led the TensorFlow API, Compiler, and Runtime teams at Google Brain and Core Systems, including the founding of TFRT and the productionization of JAX. He holds an MBA from Wharton and Computer Science MS from Penn and loves soccer, fitness, and the great outdoors.

eric@modular.com

Tim Davis

Co-Founder & President

Repeat Entrepreneur and Product Leader. Tim helped build, found and scale large parts of Google's AI infrastructure at Google Brain and Core Systems from APIs (TensorFlow), Compilers (XLA & MLIR) and runtimes for server (CPU/GPU/TPU) and TF Lite (Mobile/Micro/Web), Android ML & NNAPI, large model infrastructure & OSS for billions of users and devices. Loves running, building and scaling products to help people, and the world.

tim@modular.com

================================================================================

URL: https://www.modular.com/blog/the-worlds-fastest-unified-matrix-multiplication

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

April 20, 2023

# The world's fastest unified matrix multiplication

Abdul Dakkak

Chad Jarvis

Eric Johnson

Hengjie Wang

Ian Tramble

## "Matmul", a microcosm of AI performance

In our previous blog post, we described why AI needs to solve its compute fragmentation problem to reach its full potential and how matrix multiplication ("matmul")Â exemplifies why this remains an unsolved problem. In this post, we describe Modularâs approach to solving this problem and its game-changing benefits, including a new standard in state-of-the-art (SOTA) performance on CPUÂ as compared to existing solutions.

Before we get there, however, letâs recap where existing implementations fall short and why building a generalizable solution is so difficult. Remember, the AI industry today is bound by hardware performance and memory capacity. The result has been the development of a plethora of diverse, parallel hardware architectures and supporting highly-optimized kernel libraries by hardware vendors.

The problem for AI developers is that these kernel libraries are monolithic âpoint solutionsâ that each support only a small subset of the industry's hardware and use cases. They are often written in assembly to maximize performance, but as a result, they sacrifice composability, hackability, and portability to multiple hardware architectures. And they are large in code size due to their need to specialize on specific shapes and data types.

## A novel approach

Capitalizing on years of experience building AI infrastructure that has scaled to billions of users, Modular has invented a new & novel approach to solve this industry-wide problem. To do so, we took a complete first-principles rethink of the entire stack, and built something that is truly differentiated and unique in the industry today.

Instead of following a traditional approach of writing hard-coded kernels or a matmul compiler, we built a much more general and extensible technology that combines the best features of both approaches. This technology enables kernel authors to quickly develop high-performance kernels that span shapes, layouts, data types, and hardware architectures. Our event on May 2 (you should tune in!) and a future blog post will talk more about how our technology works, while this post focuses on the benefits and contributions of our approach.

### Unification starts with a single source of truth

If you dig into the source code for libraries such as OneDNN, you find many implementations of matmul â each hard-coded and specialized for different use cases. Youâll find one for each data type (FP16, F32, F64, Int8), various memory layouts (transposed or non-transposed), special aspect ratios (square or tall-and-skinny), various instruction set features, and more.

These fragmented point solutions make it difficult for engineers to improve the library for all possible use cases because there is too much code. This also leads to problems for users because these libraries take up a lot of disk space, swelling containers and distributions. For example, OneDNN is 51MB, MKL is 14MB, and cuBLAS is 150MB.

Modular combines what would typically be many bespoke hardware-specific implementations into a âSingle Source of Truth.â As a result, expert kernel authors can build a single composable, extensible, and portable code base across architectures and use cases. And this approach enables rapid reuse of patterns and code, applicability to optimized sub-variants of problems, and easy adoption of exotic hardware features in special cases. The Modular implementation of matrix multiplication is typically less than 100kb of machine code, which makes it practical to use in many different use cases, including mobile, web, and IoT.

### Performance portability

Implementing a performant matmul for any individual chip is challenging, as we discussed in the previous post. The challenge is compounded, however, by the adoption of heterogeneous hardware in the AI industry, including various flavors of CPUs, GPUs, TPUs, and so much more. Yet, todayâs kernel libraries only natively support a very limited number of target architectures.

For example, while OneDNN supports ARM cores, it is implemented as a wrapper around ARMâs own hardware-specific software library, ARM Compute Library (ACL). Meanwhile, OneDNN is not optimized for AMD, which has its own fork of OneDNN called ZenDNN, which leverages AMDâs Advanced Compute Library (AOCL). Mobile is another can of worms, where libraries such as Googleâs Ruy are often used.Â

All these bespoke libraries pose a significant problem for AI frameworks and, ultimately, for the users of those frameworks. Frameworks get fragmented with different variants and forks, and users must mix and match different versions with different bugs and tradeoffs. This can introduce a big gap between theoretical performance and achieved performance because users often donât know (or donât want to know!) about this level of software.

At Modular, we love all the worldâs hardware, and the generality of our approach extends well to many kinds of architectures. This allows us to provide a unified solution that defragments the framework software above the kernels. This also makes it much faster to implement high-performance support for new hardware types, with a comparably tiny engineering team and significantly less cost.

### Dynamism

Some systems use kernels that are compiled âjust in timeâ (JIT) or âahead of timeâ (AOT) by advanced AI compilers, including Googleâs XLA, Apache TVM, and OneDNN Graph. These generate kernels specialized for specific matrix sizes. This reduces the code size of the distribution but still requires many kernels to exist at execution time. Other libraries, such as MKL, special case individual matrix sizes in their kernel library that popular models use.

These challenges have become even more problematic given the rise of dynamic language models like BERT (and countless large language models, segmentation models, object detection, and so on!), which need to work on inputs (text or images) of arbitrary size. The challenge is that the system only knows the input size at inference time, not at model training or compilation time. Systems based on static shapes require padding the input or swapping many versions of a model specialized for different sizes, often leading to low performance, large code size, and model management frustration. Â Some systems try to solve this with JIT code generation, but this introduces problems with unpredictably long tail latencies.

The Modular approach completely eliminates these problems by fully supporting dynamism. Modularâs matrix multiplication and other kernels are fully dynamic shape friendly (without JIT or AOT specialization) and support other forms of dynamism (e.g., irregular control flow, unusual data types, etc.) that many existing systems struggle with. This delivers a much simpler and more predictable system overall.

### Composability

In a neural network, a matmul is seldom performed in isolation. Typically, there are other operations (e.g., activation functions or elementwise operations) that are done before and after it. It is well known that âfusingâ the code for these other operations into the matmul can produce significant performance benefits by improving memory locality and reducing dispatch overhead. There are two common approaches to address this problem â providing a limited number of pre-fused special cases or providing a domain-specific compiler to perform fusion.

The first approach is the best known and is widely used by both TensorFlow and PyTorch, as well as many other specialized frameworks (ONNXRuntime, TFLite, TensorRT, etc.). This approach is very powerful and flexible because researchers and domain experts who are not compiler engineers can extend the system. The challenge is that there are a vast number of potential combinations of operators (this is one of the reasons why TensorFlow and PyTorch have thousands of kernels!). Hand-fusing these operators further exacerbates the code size and maintainability problems discussed above.

The second approach provides a different point in the tradeoffs space â AI compilers like OneDNN graph, XLA, and NVFuser provide a wide range of kernel fusions without having to special case them all. Unfortunately, they force you to choose from a small fixed operator set without extensibility. Also, while novel fusion can provide great benefits, these compilers often donât meet the performance of traditional human-authored fused kernel libraries.

The Modular approach provides both benefits â it supports generalized fusions with a wide range of operators without having to manually write and maintain variants. More importantly, the Modular approach allows generality and extensibility without having to recompile the system and without having to be a compiler engineer. We think it will enable major new research avenues and applications by experts who may not know compiler internals.

## Unparalleled performance

While flexibility, generality, and usability sound great, they arenât worth anything if they come at the expense of performance. Performance costs directly drive operational costs, and all businesses want to be more efficient. Weâre excited to share some of our early results on CPU (GPUs are coming soon!), even though they are just the beginning for the Modular system, and we have a lot of work left to do.

For our analysis, we decided to look at a range of comparable systems available today in AWS, specifically Intel Skylake (c5.4xlarge), AMD Zen-2 (c5a.4xlarge), and Amazon Graviton 2 (c6g.4xlarge). This shows two completely different instruction sets (Intel and AMD are X86-64, Graviton is ARM Aarch64) with three major vector designs (AVX-512, AVX2, and NEON, respectively) and at three different vector lengths (512-, 256-, and 128-bits long).

We measure the Modular approach against the best-known SOTA libraries on the corresponding systems â MKL and OneDNN on Intel, AOCL on AMD (the underlying library for ZenDNN), and ACL and Ruy on ARM. We also include Eigen because it is a widely used kernel library that has been ported to many architectures. We use the latest version of each of these at the time of writing â specifically, we use MKL v2023.1.0, OneDNN v2023.1.0, Eigen v3.4, AOCL v4.0, ACL v23.02.1, and Ruy (pulled from main #363f252).

### Methodology

For our evaluation, we followed the same benchmarking methodology as Google Benchmark, where each benchmark is first warmed up and repeatedly run until 2 seconds have elapsed. For libraries that require extra setup, we perform the setup outside the main benchmarking loop. To avoid interference and increase stability, we ensure that each benchmark invocation gets a cold cache each time and disable hyperthreading to improve benchmark stability. While the modular implementation does support pre-packing, not all of the libraries we are evaluating support it. As such, to maintain fairness we do not benchmark our implementation with pre-packing enabled. We note, we have benchmarked our prepacked implementation against libraries that support prepacking and are competitive with them.

As we discussed in our previous post, matrix multiplication is used for a wide variety of use cases. For this study, we decided to measure ourselves against the most important shapes in the AI industry, which are most likely to have been optimized by existing libraries. As such, we selected matrix shapes mined from popular AI models such as BERT (with sequence lengths of 128 and 256), GPT, and DLRM. The shapes listed are in the MxNxK form, where the left-hand side operand of matmul has a size of MxK, and the right-hand side has a size of KxN. The shapes are ordered by their importance to the end-to-end model execution.

Finally, while there are many interesting data types like Int4, FP8, and bfloat16, we wanted to keep things simple and comparable. The Modular system can, of course, support any and all types out there, but for this analysis, we focus on FP32, which is widely used and tuned by each implementation we reference.

### Performance results

With this in mind, we start by looking at the single-threaded performance on the Intel Skylake (c5.4xlarge) system. Single-threaded performance doesnât utilize the entire chip but helps normalize results (i.e., removing higher-order factors like multi-processing, false sharing, NUMA issues, etc.). Beyond that, it forms the foundation of multi-threaded performance and is essential for certain use cases in mobile and game engines.

The figure below shows the performance in GigaFLOPs per second (GFLOP/s) of the Modular approach and other SOTA implementations for the Intel system â MKL, OneDNN, and Eigen. The Modular matmul implementation achieves performance that is on par or better than SOTA existing solutions. In fact, we are roughly 1.5 times faster than OneDNN on this Intel system.

While the single-threaded performance is a useful datapoint, full multi-threaded performance is what typically matters for server use cases. This also puts much more stress on the machine, as a full peak implementation can run into limitations like peak FLOP/s, DRAM bandwidth, cache utilization, etc. Below we show Modularâs performance against these systems â Modular is 1.46 times faster than OneDNN, which is a remarkable achievement given the generality and other benefits we discussed before.

While strong results on one hardware platform are important, a fundamental value proposition of the Modular implementation is that a single source of truth can deliver high performance across a wide range of hardware. Letâs look at Modularâs performance on AMD hardware, when compared to the AOCL library (which is the SOTA on AMD and is the backbone of ZenDNN) and the OneDNN library we saw above.

Looking at the graph below, you can see that the performance of OneDNN does not translate to AMD hardware, and while the AOCL library provides significant uplifts, the Modular approach is approximately 1.6 times faster than SOTA on the AMD system.

We also performed the same experiment on the Amazon Graviton 2 system, this time including the Ruy and ACL libraries. Ruy is the library used by edge frameworks such as TensorFlow Lite, and ACL is the backbone of the OneDNN support for ARM.

Even though ACL and Ruy are both competitive on ARM, the Modular implementation achieves significantly better performance on average â 1.8 times better than ACL and 1.2 times better than Ruy.

In addition to comparing different implementations of matrix multiplication on a given system, it is also interesting to cross-compare the absolute performance of these different systems. These are very complicated machines with a lot of moving parts, but we can look at things at a coarse grain. The Intel system benefits from having 512-bit long vectors instead of shorter 256- or 128-bit vectors. The Graviton 2 system performs well despite a shorter 128-bit vector because it has 16 physical cores, compared to the 8 physical cores on the Intel and AMD systems.

### Kernel fusion aware

Finally, to demonstrate the composability of the implementation, we will look at how matmul can be fused with other operations. We want to compare against common operations that other implementations have highly tuned, so we use a âfully connectedâ (FC) block, defined by the equation "activation(matmul(A, B)+bias)," where activation is an activation function (we use Relu below). Libraries such as OneDNN have fused paths for the FC block, and to make the results fair, we do not compare against libraries that do not provide a way to define the FC block in a fusible fashion. Therefore, in this analysis, we only compare against OneDNN and Eigen, since they provide a way to express the fusion patterns. For consistency of presentation, we use the same shapes and three hardware configurations as above.

Below we can see the performance of the FC block on the Intel Skylake architecture. Despite the generality and flexibility of the Modular approach, it sets a new SOTA, outperforming OneDNN by 1.45 times and Eigen by 1.8 times on average.

We see similar strong results on the AMD system, where the Modular approach delivers a 2.1 times performance advantage over OneDNN and 2.3 times performance improvement over Eigen.

The Amazon Graviton 2 system is a significantly different architecture, and the Modular stack is not as tuned as it is for X86-64. Still, even here, we can see that Modular delivers a 1.3 times performance improvement over Eigen and 1.1 times over Ruy. OneDNN/ACL does not provide a fused FC layer for ARM systems.

As we can see from the data above, kernel fusion can provide significant performance uplifts when implemented right, and the benefits are even more significant as the fusion region grows. Modularâs approach was built to embrace fusion from the beginning, which allows it to support a very general set of fusions (i.e. far beyond elementwise operations, and not limited to matmul). We think that that delivering a single source of truth implementation that is performance portable, dynamic, and composable is a key contribution that will enable new research and production use cases.

## Whatâs next

While we are excited about our performance results, the most important thing about them is that we can achieve them without compromising on our original goals and that these are just the beginning! The Modular matmul has a single source of truth, and supports many different architectures, dynamic shapes, and extensible fusions. Beyond delivering many âtodayâ benefits to our users, the generality of our architecture allows us to radically simplify the stack above, produce a more predictable user experience and enable rapid bring-up of new hardware in a way that people havenât experienced before.Â

It is also worthwhile to emphasize that todayâs existing SOTA implementations are the product of decades of research and development by many incredibly talented engineers. Modular has a talented but comparatively small team and has been able to deliver strong results quickly because of key technology advances in our stack. But even more than that - itâs also the result of a first-principles rethink and a willingness to truly invest in a ârebuild from the bottom-upâ approach that Modular was founded on.

This is all part of our broader vision to enable AI to be used by anyone, anywhere, and enable AI to truly impact the world in a more meaningful and useful way. By building and creating novel approaches to AI infrastructure, we imagine a world where we can help push forward the entire industry to enable it to develop and deploy AI systems faster, more efficiently, safer, and ultimately be more accessible to the whole world. We hope to empower the entire hardware industry to build new and novel compute architectures that drive hardware innovation forward through software.

While we show matmul performance in this blog post, we have applied the same methodology across the stack. Tune in to the launch to see how these improvements translate to end-to-end performance and the technology that enables this. We are excited to explain more about how it works soon. Sign up for our upcoming May 2, 2023 launch event at Modular.com to learn more. Additionally, Modular is growing its exceptional team â if you are interested in building and driving forward the state of the art of AI infrastructure, please check out the posts on our careers page.

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Engineering

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Abdul Dakkak

AI Compiler Engineer

Expert in machine learning, compilers, programming languages, and accelerated computing. Before Modular, Abdul led the development of AI compilers for GPUs at Microsoft Research and the Mathematica Compiler at Wolfram Research. Abdul has developed open-source tools for accelerating real-world applications to optimize their performance across the hardware and software stack.



Chad Jarvis

AI Performance Engineer

Chad has a strong background in low-level code optimization, parallel programming, and high performance computing. He has worked for more than 17 years in research and engineering in Europe and North America. He holds a PhD in particle physics and is one of the authors of the Higgs Boson discovery. Chad has a passion and focus for understanding things at a fundamental level. Prior to joining Modular he worked at Graphcore working closely with the hardware engineers to implement and optimize custom features of the GC hardware, and before that Simula Research Laboratory among many other distinguished research facilities. He enjoys spending his spare time with his family, travelling, collecting rare coins, and eating excellent food.



Eric Johnson

Product Lead

Product leader who has built and scaled AI applications and infrastructure. Eric led the TensorFlow API, Compiler, and Runtime teams at Google Brain and Core Systems, including the founding of TFRT and the productionization of JAX. He holds an MBA from Wharton and Computer Science MS from Penn and loves soccer, fitness, and the great outdoors.

eric@modular.com

Hengjie Wang

AI Performance Engineer

Hengjie Wang is a software engineer focusing on performance optimizations for AI and scientific applications. He has many years of experience in developing and optimizing large-scale scientific applications on world-ranking supercomputers. He has also developed Deep learning algorithms to advance physical simulations. Before joining Modular, he was a postdoctoral scholar in the Lawerence Berkeley National Lab, where he participated in developing the Exa-scale projects MFIX-Exa and AMReX on national supercomputers. He is a big fan of Go and enjoys hiking and dog training.



Ian Tramble

AI Performance Engineer

Experienced systems software engineer with a background in performance and accelerated computing. Before joining Modular, Ian spent 5 years at NVIDIA working on MLPerf Inference, TensorRT, and systems software for autonomous vehicles. He is passionate about providing great out-of-the-box performance by abstracting hardware. Ian graduated from the Engineering Science program at the University of Toronto with a major in electrical and computer engineering.



================================================================================

URL: https://www.modular.com/engine

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/engine#performance

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/get-started

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

80K+ developers building with MAX

# A high-performance generative AI framework

MAX replaces and simplifies your AI inference stack, maximizing performance and reducing cloud costs

## Achieve state of the art NVIDIAÂ GPU performance

Unlock state of the art latency and throughput without writing low-level CUDA code.

### Deploy MAX on GPUs

## Optimize your existing PyTorch & ONNX models

Migrate seamlessly without rewriting your AI models and pipelines on a unified AI stack.

## Use Mojo to supercharge your AIÂ applications

Extend your PythonÂ code with high-performance Mojo, a new programming language with the expressiveness of Python and the performance of C.

## Develop locally, deploy globally to any cloud.

Develop your AI applications locally and package and deploy across any cloud provider, on CPUs and GPUs, without having to change your code.

## Run Gen AI locally underÂ 3Â minutes

Download MAX first to run an LLM

- Run Llama3-8B in your CLIGet started

- Run Llama2-7B in your CLIGet started

- Run Replit-3B in your CLIGet started

Run Llama3-8B in your CLI

Run Llama2-7B in your CLI

Run Replit-3B in your CLI

## Free Forever

The MAXÂ framework is a free and open platform for you to develop and deploy AI inference workloads.

A new framework for Gen AI, and the best way to deploy PyTorch

Development tools for accelerated compute on GPUs and CPUs, built from the ground up for GenAI, but compatible with today.

## Developer Approved ð

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âMax installation on Mac M2 and running llama3 in (q6_k and q4_k) was a breeze! Thank you Modular team!â

NL

Mojo is Python++. It will be, when complete, a strict superset of the Python language. But it also has additional functionality so we can write high performance code that takes advantage of modern accelerators.

jeremyphoward

âTired of the two language problem. I have one foot in the ML world and one foot in the geospatial world, and both struggle with the "two-language" problem. Having Mojo - as one language all the way through is be awesome.â

fnands

âMojo can replace the C programs too. It works across the stack. Itâs not glue code. Itâs the whole ecosystem.â

scrumtuous

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

âWhat @modular is doing with Mojo and the MaxPlatform is a completely different ballgame.â

scrumtuous

âI am focusing my time to help advance @Modular. I may be starting from scratch but I feel itâs what I need to do to contribute to #AI for the next generation.â

mytechnotalent

âMojo and the MAX Graph API are the surest bet for longterm multi-arch future-substrate NN compilationâ

pagilgukey

âA few weeks ago, I started learning Mojo ð¥Â and MAX. Mojo has the potential to take over AI development. It's Python++. Simple to learn, and extremely fast.â

svpino

âMojo destroys Python in speed. 12x faster without even trying. The future is bright!â

svpino

âItâs fast which is awesome. And itâs easy. Itâs not CUDA programming...easy to optimize.â

dorjeduck

âI tried MAX builds last night, impressive indeed. I couldn't believe what I was seeing... performance is insane.â

drdude81

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

"Mojo gives me the feeling of superpowers. I did not expect it to outperform a well-known solution like llama.cpp."

Aydyn

"C is known for being as fast as assembly, but when we implemented the same logic on Mojo and used some of the out-of-the-box features, it showed a huge increase in performance... It was amazing."

Aydyn

âI'm excited, you're excited, everyone is excited to see what's new in Mojo and MAX and the amazing achievements of the team at Modular.â

Eprahim

âI'm very excited to see this coming together and what it represents, not just for MAX, but my hope for what it could also mean for the broader ecosystem that mojo could interact with.â

strangemonad

It worked like a charm, with impressive speed. Now my version is about twice as fast as Julia's (7 ms vs. 12 ms for a 10 million vector; 7 ms on the playground. I guess on my computer, it might be even faster). Amazing.

Adalseno

âThe more I benchmark, the more impressed I am with the MAX Engine.â

justin_76273

## Get started now

- Getting started guideStep by step walkthrough on how to get started

- Browse examplesRun a project locally in just 2min

- Connect with buildersJoin over 22,000 developers

Getting started guide

Step by step walkthrough on how to get started

Browse examples

Run a project locally in just 2min

Connect with builders

Join over 22,000 developers

Download for your platformÂ now

- 

- 

- 

View Pricing

MAX for Enterprise

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

MAX on GPU waitingÂ list

Be the first to get lightning fast inference speed on your GPUs. Be the envy of all your competitors and lower your compute spend.

================================================================================

URL: https://www.modular.com/team/eric-johnson

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?author=Eric+Johnson

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/vision

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# Build AI for anyone, anywhere.

## Our mission is to have real, positive impact in the world by reinventing the way AI technology is developed and deployed into production with a next-generation developer platform.

We believe that AI is a net positive force in the world and will help transform it for the better.

Our vision and mission are to reinvent AI infrastructure to advance humanity.

### We aim to push the whole world of AI forward, not just aÂ select few companies andÂ products.

After working for years at the worldâs largest technology companies, scaling the world's largest AI work loads, building the hardware that powers them, and deploying AI to billions of mobile phones and edge devices, we saw that fragmentation and technical complexity held back the impact to a privileged few.

We need to rethink the current AI systems and infrastructure from first principles to make it easy for anyone to leverage AI to solve the worldâs most critical problems with software that just works.

Modular and composable infrastructure that simplifies AI development and deployment is what the world needs.

Together, as Modulers, we are a team of the world's best AI infrastructure leaders who are reinventing and rebuilding accelerated compute for everyone.

### How it started

Chris Lattner and Tim Davis met at Google and felt AI was being held back by overly complex and fragmented infrastructure. Motivated by a desire to accelerate the impact of AI on the world by lifting the industry towards production-quality AI software, they founded Modular.

Leadership team

Chris Lattner

Co-Founder & CEO

Tim Davis

Co-Founder & President

Adin Scannell

Cloud Infrastructure Lead

Eric Johnson

Product Lead

Joe Pamer

Mojo Distinguished Engineering Lead

Kalor Lewis

VP, Finance

Mike Edwards

Operations Lead

Mostafa Hagog

Performance & Compiler Engineering Lead

Nick Kreeger

Framework Engineering Lead

## Backed by the best investors in AI

Our goal is as enormous as it is profound. Weâre building a different kind of company to achieve this.

We have assembled the best AI software and hardware leaders, and are systematically rebuilding the AI software stack from the ground up.

ð Weâre hiring!

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Chris Lattner

Co-Founder & CEO

Distinguished Leader who founded and scaled critical infrastructure including LLVM, Clang, MLIR, Cloud TPUs and the Swift programming language. Chris built AI and core systems at multiple world leading technology companies including Apple, Google, SiFive and Tesla.

clattner@modular.com

Tim Davis

Co-Founder & President

Repeat Entrepreneur and Product Leader. Tim helped build, found and scale large parts of Google's AI infrastructure at Google Brain and Core Systems from APIs (TensorFlow), Compilers (XLA & MLIR) and runtimes for server (CPU/GPU/TPU) and TF Lite (Mobile/Micro/Web), Android ML & NNAPI, large model infrastructure & OSS for billions of users and devices. Loves running, building and scaling products to help people, and the world.

tim@modular.com

Adin Scannell

Cloud Infrastructure Lead

As a hands-on technical leader, Adin is passionate about building products and technologies that challenge the status quo. He previously co-founded GridCentric which was acquired by Google, where he worked on problems at scale for nine years across Google and Google Cloud. He has spent the last two decades solving problems across the infrastructure stack â from hypervisors and operating systems to containers and security.



Eric Johnson

Product Lead

Product leader who has built and scaled AI applications and infrastructure. Eric led the TensorFlow API, Compiler, and Runtime teams at Google Brain and Core Systems, including the founding of TFRT and the productionization of JAX. He holds an MBA from Wharton and Computer Science MS from Penn and loves soccer, fitness, and the great outdoors.

eric@modular.com

Joe Pamer

Mojo Distinguished Engineering Lead

Joe spent the last 8 years at Meta, where he was focused on improving the companyâs AI and developer infrastructure stacks. Before that he served as Instagramâs CTO, overseeing much of Metaâs work on programming languages (mainly focused on Python, PHP, and Javascript), and co-founded its first applied ML team for infrastructure. Before joining Facebook, he helped design and develop the F#, TypeScript, and Swift programming languages, and contributed to many other developer technologies, such as Clang, Visual Studio, and VS Code.



Kalor Lewis

VP, Finance

Kalor is Modular's VP, Finance and leads all our Finance operations. Prior to Modular, Kalor was a VP, Finance at Fivetran where he was the first finance hire in 2018 and built out the companies entire finance function. Before Fivetran, Kalor was part of Palantir Technologies, where he scaled their strategic finance function.



Mike Edwards

Operations Lead

Mike has spent over 25 years working in the fields of IT, corporate operations, and software development - most recently at Apple. Mike volunteers his time serving as a Board member with the LLVM Foundation, focusing on finance and operations. Mike truly believes in the power of AI to help address some of the worldâs greatest needs.



Mostafa Hagog

Performance & Compiler Engineering Lead

Mostafa is a seasoned engineering leader in high-performance computing. During his tenure at NVIDIA, he served as Engineering Director and led teams to develop optimized deep learning libraries like cuDNN and CUTLASS, revolutionizing GPU-accelerated AI. At SiFive, as VP of Software, Mostafa assumed a leadership role guiding teams in the development of an MLIR/LLVM-based software stack for SiFive Intelligence & performance cores. His contributions also extend to optimizing Intel GPU hardware/software features, playing a pivotal role in developing the AVX1/2 SIMD ISA for Intel CPUs, and contributing to the GNU C Compiler. Mostafa holds a Master of Science in Electrical Engineering from the Technion, with a specialization in compiler optimizations. His unwavering passion for innovation continues to drive advancements in the field of high-performance computing.



Nick Kreeger

Framework Engineering Lead

Software Engineering lead with over 15 years of experience working at Google, Microsoft and a handful of startups. Nick has contributed to many technologies in Machine Learning such as TensorFlow.js, TensorFlow Lite/Micro, and ONNX/ONNXRuntime. Nick enjoys spending his free time with family and enjoying the Minnesotan outdoors.



================================================================================

URL: https://www.modular.com/blog/whats-the-difference-between-the-ai-engine-and-mojo

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

July 11, 2023

# Whatâs the difference between the AI Engine and Mojo?

Eric Johnson

Shashank Prasanna

âOn May 2nd, we announced our next-generation AI developer platform with two exciting breakthrough technologies â theÂ Mojo programming languageÂ and theÂ Modular AI Engine. In just over two months, more than 110k developers haveÂ signed upÂ for theÂ Mojo PlaygroundÂ to learn Mojo and experience its performance firsthand, over 30k developers have signed up to our waitlist for the AI engine, and ourÂ Modular communityÂ onÂ DiscordÂ has grown to 17k developers! Weâre incredibly excited to see developers sharing their experience with Mojo, providing product feedback, and learning from each other.

Since our announcement, a question we continue to receive frequently from our community members is âÂ whatâs the difference between the AI Engine and the Mojo programming language?

Weâre glad to see questions from our community members and their enthusiasm to learn more about these products. Weâll first discuss the AI Engine, which will be less familiar to our readers, given that it is still in closed preview. We will then discuss how Mojo integrates with the AI Engine and finally wrap the post up with how we think theyâll join forces to unlock many AI and other use cases that are bottlenecked by performance and usability constraints.

## What is the AI Engine?

The AI Engine is a high-performance inference engine for leading AI frameworks like PyTorch and TensorFlow. It supports importing trained model artifacts from TensorFlow (SavedModel), PyTorch (TorchScript), and ONNX and uses state-of-the-art compiler and runtime technologies to deliver up to 7.5x higher throughput vs. native framework runtimes. Our earlier blog post shared its performance on TensorFlow and PyTorch models across different CPU architectures (Intel, AMD, ARM). For the most up-to-date performance benchmarks, visit performance.modular.com. The AI Engine is still in closed preview â you can sign up here to be considered for early access.

â

Under the hood, like most compiler toolchains, the AI Engine has four key components:

- TheÂ ImporterÂ takes a serialized model (e.g., a TensorFlow SavedModel) and converts it to a graph representation that the compiler can optimize.

- TheÂ Graph OptimizerÂ takes this graph representation and performs graph-level optimizations (e.g., constant folding, shape inference).

- TheÂ Kernel GeneratorÂ produces high-performance machine learning kernels tailored to individual architectures and microarchitectures, including advanced techniques like operator fusion.

- The Runtime executes the optimized kernel graph with low overhead to deliver state-of-the-art performance.

For the user, the AI Engine offers both Python and C/C++ APIs and integrates into popular serving frameworks like NVIDIAâs Triton Inference Server and TensorFlow Serving to deliver high-throughput inference in production. You can read more about how it achieves that in this blog post. The AI Engine is still in closed preview - sign up here to express interest in testing the AI Engine.

### How Mojo integrates with the AI Engine

The AI Engineâs kernel generator generates hardware-specific optimized implementations of machine learning operators. Modular engineers initially wrote this code inÂ MLIRÂ directly but found it to be a productivity challenge. We decided things would be much more productive in a high-level language with Python-like usability and safety and predictability guarantees of systems programming languages like C and Rust. As such, Mojo was born to satisfy the needs of Modularâs internal development efforts on the AI Engine.

Today all of the AI Engineâs optimized kernels are written in Mojo, allowing the Engineâs capabilities to be extended to support new and exotic model architectures where custom kernels for operators can be authored directly in Mojo without resorting to low-level C, C++, or CUDA programming. While you can use the AI Engine as a drop-in replacement for your existing models without Mojo, if youâre an AI developer creating custom operators and model architectures, Mojo can dramatically improve your productivity in authoring custom kernels enabling you to deploy models to production in the shortest possible time.

Sign up for early access to the AI Engine and learn more:

- AI Engine overview: https://www.modular.com/engine

- AI Engine performance dashboard: https://www.modular.com/engine

- Modular launch blog post: A unified, extensible platform to superpower your AI

- Model serving blog post: Accelerating AI model serving with the Modular AI Engine

- Documentation: https://docs.modular.com/engine/

## What is Mojoð¥ ?

While Mojoâs origin story was for authoring kernels for the AI Engine, thats not its only purpose. Mojo is designed to become a superset of Python over time by preserving Pythonâs dynamic features while adding new primitives for systems programming.

Itâs a brand-new programming language that combines the usability of Python with powerful compile-time metaprogramming, integration of adaptive compilation techniques, caching throughout the compilation flow, and other features that are not supported by existing languages. In his blog post, Jeremy Howard described Mojo as âPython++â since it enhances Pythonâs capabilities, enabling it to enter new domains.

Mojo is still in early development, but the value of Mojo is in its âdepthâ as a language. Mojo is not the first programming language to execute Python-like syntax on accelerators, but it is the first to enable high-level AI developers direct low-level control over hardware â unlocking researchers to innovate at all levels of the stack.

â

Mojo has a syntax that looks like Python, but unlike the default âCPythonâ implementation, which uses an interpreter, Mojo is compiled. The Mojo compiler lowers Mojo code, performs optimizations, and generates code for specific hardware targets. Mojo also offers low-level types like the SIMD type, which represents a low-level vector register directly in hardware for writing hardware-optimized code.

Mojo has already had its first success story â the AI Engine. The AI Engineâs performance results on theÂ performance dashboardÂ are the result of the improved engineering velocity of the AI Engineâs team in supporting popular models on a variety of hardware to deliver state-of-the-art performance. Of course, Mojo was not only built for Modularâs use cases, but our community users have already started creating amazing projectsÂ like mojo-libc.

You can get started with Mojo today on the Mojo Playground environment, which allows you to walk through Jupyter Notebook examples, create your own and share them with the Mojo community on Discord. One of the most frequently requested features is the ability to download Mojo and run it on your local computer â weâre working on this, so stay tuned!

Here are additional resources to get started with Mojo:

- Mojo overview: https://www.modular.com/mojo

- Mojo Playground: https://playground.modular.com/

- Modular launch blog post: A unified, extensible platform to superpower your AI

- LLM language post: Do LLMs eliminate the need for programming languages?

- Documentation: https://docs.modular.com/mojo/

## Mojo and the AI Engine - better together!

The AI Engine and Mojo were designed to be used together. In the near term you will be able to use the AI Engine to deploy widely used models for production and deliver high-performance, low-latency inference on a range of hardware architectures. With Mojoâs Python-like usability, you can easily extend the AI Engineâs capabilities for tasks such as writing performant pre- and post-processing operations (e.g. tokenization, image transformations, non-max suppression, etc.), replacing high-latency operations and custom fused kernels to deliver even greater performance. Together the AI Engine and Mojo will allow you to accelerate all parts of your AI development pipeline.

In the longer term, we expect the AI Engine and Mojo together to accelerate workloads in several domains beyond AI. Python is used in multiple domains including data analytics, image and signal processing, scientific and numeric computing, gaming, 3D graphics, network programming, database access and others. As AI proliferates into these and other domains, we expect the AI Engine and Mojo to play an important role in accelerating applications in these domains and unlock the next wave of AI innovations.

Ultimately they are productivity tools in a developerâs toolbox, and a toolâs potential is only limited by the userâs creativity. Weâre excited to see what challenges youâll solve with them and what new innovations theyâll help you unlock. We look forward to hearing from you about your use-cases, feature requests and product feedback.

In the meantime:

- Head over to the Mojo Playground to test the latest release of Mojo right now!

- Sign up here to express interest in testing the AI Engine

- Join our warm, welcoming and awesome community on Discord

- Discuss Mojo and the AI Engine topics on GitHub discussions

Until next time ð¥!

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Product

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Eric Johnson

Product Lead

Product leader who has built and scaled AI applications and infrastructure. Eric led the TensorFlow API, Compiler, and Runtime teams at Google Brain and Core Systems, including the founding of TFRT and the productionization of JAX. He holds an MBA from Wharton and Computer Science MS from Penn and loves soccer, fitness, and the great outdoors.

eric@modular.com

Shashank Prasanna

AI Developer Advocate

Shashank is an engineer, educator and doodler. He writes and talks about machine learning, specialized machine learning hardware (AI Accelerators) and AI Infrastructure in the cloud. He previously worked at Meta, AWS, NVIDIA, MathWorks (MATLAB) and Oracle in developer relations and marketing, product management, and software development roles and hold an M.S. in electrical engineering.



================================================================================

URL: https://www.modular.com/blog/outperforming-rust-benchmarks-with-mojo

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

February 2, 2024

# Community Spotlight: Outperforming Rust âï¸ DNA sequence parsing benchmarks by 50% with Mojo ð¥

Mohamed Mabrouk

Editor's note: This article received a lot of attention from the Mojo, Rust, and bioinformatics communities, and we received strong feedback about the performance numbers and methodology after its initial publication. We followed up with a post comparing Rust and Mojo, and the original author has published updated performance data to the BlazeSeq repository. We want to thank everyone for their thoughtful feedback. While the data and performance numbers in this article are out of date, for full transparency we're leaving the original text in place.

### The era of big-data in bioinformatics

The challenges for bioinformatics in the modern day, are rooted in big-data manipulation. Thousands of multi-million dollar DNA-sequencing machines are working non-stop in all fields of biotechnology, medicine, and biomedical research. The annual sequencing data size is expected to be up to 40 exabytes of raw sequences by 2025. That's 20x the data uploaded to YouTube every year.

While most of the final analysis is carried out in high-level languages like Python and R, the world of bioinformatics is powered by an underlayer of black magic! Highly-optimized tools written in C, C++, and Java that pre-process and summarize large amount of raw data. This creates a two-world problem where bioinformaticians who are not skilled in low-level languages, are prohibited from understanding, customizing, and implementing low-level operations. In addition, typical bioinformatic pipelines are a mixture of Bash and Python scripts calling into pre-compiled binaries, along with the analysis logic itself. It's becoming increasingly complex and frustrating for new and experienced bioinformaticians. This is the same issue that the AIÂ community is facing.

### Mojo ð¥ one tool to rule them all

I first heard about Mojo from the demo video by Jeremy Howard. Its value offer is simple, a Pythonic language that allows the programmer to optimize at a much lower level, to unify the fragmentation in fields such as AI. Learning Mojo was relatively easy for me, coming from the Python world, I got used to the extra syntax in only a few days. I decided to try Mojo ð¥ in a serious project for a low-level bioinformatic task; FASTQ parsing and quality trimming. FASTQ is a basic format for most DNA sequencing operations, incorporating both the genomic sequence and confidence scores of the machine in each base call. It is a simple format to parse, with most records looking like this:

However, typical uncompressed file sizes are 1-50 GB, an average sequence-heavy study could generate north of 1 TB for a single file.Â Performance is critical in parsing and data manipulation.

I tried to write a simple parser that would:

1. Read a chunk of the file as a String.2. Split the string on the newline \n separator.3. Take each 4 lines, validate that they are a consistent and correct FASTQ record, and return it.4. Rinse and repeat until reaching EOF.

On the first try, MojoFastTrim ð¥ achieved 8x the performance of python's SeqIO. I was pleasantly surprised with the development time. My code was still Pythonic, concise at around 200 lines, and using features the average python developer would understand. In quality trimming, where low quality bases are removed from each read, it achieved 50%-80% of the industry standard tool Cutadapt. This was a surprising level of performance for development time I put into the project.

### Going down the optimization rabbit hole

The most powerful benefit of Mojo ð¥ is that it gives you access to low-level optimizations. The nascent state of the Mojo standard library meant that I had to write, test, and benchmark some functions from the ground up. Mojo's first-class support of SIMD vectorization was really helpful and surprisingly intuitive. Here is the implementation of the vectorized version of a function to find the index of the newline separator in Mojo:

##### Iterative

##### SIMDÂ Vectorized

The vectorized version loads 32-elements of Int8 and checks the presence of a new-line separator using fewer operations.

In the following graph, you can see the effect of SIMD vectorization. It provides up to 4x speed up, with average speed up of 3.2x. Similarly, SIMD storing and loading from tensors providers substantial performance gains.

In addition, I explored optimizations from C/C++ implementations. I was concerned that no explicit memory buffer was allocated for the loaded chunks, but the Mojo compiler was already taking care of that and avoiding new memory allocations:

Implementing those optimizations resulted in an extra 3x speedup, and MojoFastTrim ð¥ was on average 24x more performant than Python's SeqIO. In addition, due to control over reference and value semantics in Mojo ð¥ I applied a FastParser version of the parser. No memory copies are made during parsing and the individual reads are passed around as references to the loaded chunk in memory. This approach is implemented in Rust's needletail parser. Although Mojo is still a young language, my implementation was 50% faster than the Rust implementation on Apple Silicon, and 100x faster than SeqIO. In quality trimming, MojoFastTrim ð¥ was on average 2x faster than the highly-optimized Python/Cython Cutadapt.

This benchmark can be reproduced by following the instructions here.

### Final thoughts

For Python programmers wanting to write more performant code, Mojo is a great tool to try, and easy to learn. However, the language and the ecosystem is still growing, I had to use print debugging to gain insight into the bugs I was encountering. The debugger is still in preview and undocumented, although they tell me it will be officially launching soon!

In conclusion, I think that Mojo can be a radical change for a wide range of Python trained scientists and researchers across many fields. It can enable them to have a level of performance and control, that was previously unachievable.

Thanks for reading!

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Community

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Mohamed Mabrouk

Community Guest



================================================================================

URL: https://www.modular.com/blog/increasing-development-velocity-of-giant-ai-models

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

August 12, 2022

# Increasing development velocity of giant AI models

Eric Johnson

This post is part one of a two-part series on how we are improving productivity for engineers and scientists for those developing large models. Subscribe to our newsletter to get future posts delivered to your inbox!

Machine learning models are getting larger and larger â some might even say, humongous. The worldâs most advanced technology companies have been in an arms race to see who can train the largest model (MUM, OPT, GPT-3, Megatron), while other companies focused on production systems have scaled their existing models to great effect. Through all the excitement, whatâs gone unsaid is the myriad of practical challenges larger models present for existing AI infrastructure and developer workflows.

One of the many challenges of developing large models is the painful experience of working with tooling that isnât equipped to deal with models with huge weights â which can be upwards of 100+ gigabytes. An unspoken truth is that AI deployment engineers often have to wait minutes or even hours for tooling to work with large models. This isnât great for productivity, isnât how we want AI specialists to spend their time, and reduces the ability to iterate rapidly.

At Modular, we recognize that developer productivity is a significant part of the cost of training and deploying models. We are constantly optimizing our toolchain to improve the lives of our early customers and our internal developers. This post discusses the technical challenges of managing many gigabytes of data in the compilation process and the changes we have made to our infrastructure (and the MLIR compiler framework) to solve them.

## Working with AI models is uniquely challenging

If you arenât familiar with graph transformations, optimizations, and compilers in machine learning, there is no need to be intimidated.Â  These are techniques used to increase the performance and portability of an AI model or enable it to deploy to some target hardware. There are high-level âcompilersâ like the TensorFlow Lite Converter that transforms and optimizes a TensorFlow SavedModel into a highly optimized program format (i.e., FlatBuffer) for execution on edge devices. There are also domain-specific compilers like XLA and TorchScript JIT Compiler. They create or consume an intermediate representation (e.g., a âgraphâ) of an AI model and compile it to another format - for example machine code or a domain specific runtime representation (e.g. CUDA graphs).

Compiling an AI graph is actually quite different from traditional compilation problems. An AI graph contains two things: 1) the graph topology (how the layers are interconnected) and 2) the model weights (parameters associated with specific layers). In terms of size, the graph topology is on the order of kilobytes, whereas weights are on the order of megabytes and gigabytes. For example, look at some of the bigger models released by Meta. The Open Pre-trained Transformers have 30B, 66B, or even 175B+ parameters, which equates to 100+ gigabytes of weights. There are even larger models like Gopher or Megatron too.

â

â

Large models are not handled well by existing tools in the AI ecosystem. For example, protobufs have an inherent 2-gigabyte limit, creating problems for model formats backed by this serialization format. In the latest version of TensorRT, âtransformer-based networks such as BERT and GPT, can consume CPU memory up to 10 times the model size during compilationâ which makes it difficult to use for large models. In ONNX, users must split the model weights across many files to support large models. All this introduces unnecessary complexity into the AI development workflow, can lose the âsingle source of truthâ for a model, and generally makes it harder to distribute models.

To compound the situation, the heft of these weights can lead you to add workarounds that complicate your overall AI development workflow. For example, at Modular, we built a mechanism for caching temporary files because certain compiler stages were taking 2+ minutes â which broke our developers out of interactive flow state.

Like other workarounds, we realized that this caching was a âduct tape solutionâ: it wasnât 100% reliable and didnât help when the cache missed. Because we care so much about developer productivity, we decided to tackle the core of the problem.

## MLIR in the Modular compilation stack

The Modular stack leverages the MLIR compiler infrastructure to represent and transform AI models, including AI operator graphs (for multiple frameworks), mid-level runtime primitives, and low-level machine code generation. Our team has many of the foundational architects of MLIR, who were deeply involved in releasing MLIR to the world, and we continue to actively maintain large portions of core MLIR today.

MLIR is a sub-project of the LLVM compiler infrastructure project, which provides a modern toolkit for building domain-specific compilers. It provides a set of core building blocks necessary for modeling, analyzing, and transforming a wide range of computational domains including hardware design, quantum, artificial intelligence among many others.

MLIR has allowed us to build a single cohesive system that spans the entire stack, which is more powerful, layered, extensible, and easier to maintain than conventional stacks. Using unified infrastructure enables our improvements to easily transfer across our tooling stack and enables greater composability and modularity of our entire development workflow.

Modular is not alone in leveraging MLIR â many other systems use MLIR for representation and transformation, including TensorFlow, XLA, PyTorch, ONNX, etc. As this ecosystem continues to grow, we can all celebrate MLIRâs benefits, but we must also continue to invest in its evolution.

## MLIR is a good thing, but its approach for managing weights was not!

One of the fundamental building blocks of MLIR is an Attribute, which you can think of as a form constant data that is âuniqueâdâ (aka, memoized, or internâed). Attributes are user extensible, meaning they may take various forms depending on the use case. Attributes are used for things like constant expression values (e.g. â5â, â10.0â, etc.), string literals, for enumerators (e.g. âless thanâ, âgreater thanâ, âequal toâ, etc.), for arrays of data â¦ and far more. Most MLIR-based AI tooling uses attributes to hold weights for AI models.

However, this is a problem: model weights can be enormous, and MLIR stores a two-gigabyte weight tensor the same way as a four-byte tensor â in an attribute containing a uniqueâd array of elements. This creates an obvious problem given we just used the words uniqueâd and gigabytes so close together!

Here is the challenge: when something is uniqueâd in MLIR, it is allocated, hashed, and stored within an "MLIRContext". These objects have lifetimes attached to the MLIRContext, and they are not destroyed until the context is destroyed. This is great for small values because we can pass them around and compare unique'd objects by pointer, share allocations for attributes (very common), and more.

These benefits turn into a liability with huge weight tensors: we donât want to reallocate, copy, or unique them. We also donât want them to live forever: it is important to deallocate big weights when the computation no longer references them. For example, when we run a tool that quantizes our model, it needs to transform the operator graph and generate new weights â and can end up with multiple copies of that data which all live for the duration of the compilation process.

Another problem for ML tooling is how MLIR was serialized to the file system. When we started, MLIR had no binary serialization format - just a textual format. This is a problem for large weights because each byte of binary data ended up being emitted in a hexadecimal form - taking 2x the space as the data it is encoding. That means that we end up not only taking a long time to create the hex (about 20 seconds for a decently sized multi-gigabyte model), but our intermediate files are twice as big as they should be - 2x an already big number!

## A bigger impact than just developer productivity

This well-intended design mechanism can cripple even the best compilers. The most obvious challenge is that it compounds the time necessary to compile, inspect, and transform a model. If you have ever used the excuse, "My code's compiling," you'll be aware of the pain this creates. Here, we are forcing the processor to continuously allocate, copy, and hash multiple gigabytes of data.

A bigger problem than compile-time is that memory use impacts larger scale architectural features in the Modular stack. For example, because our compiler and technology stack itself is highly parallel and utilizes advanced features like online search, memory use directly affects the amount of work we can do in parallel. This is important to get the highest quality of results.

At Modular, it is core to our ethos that we build tools that users will fall in love with. We realize that advanced features simply wonât get used if they are difficult to use, impact productivity, or have significant caveats (e.g. they donât work in all cases). We love that fixing these foundational problems with large weights, allows us to subtract complexity from our users lives and workflows.

## Fixing this the right way: core additions to MLIR

The team at Modular are prominent stakeholders and maintainers of MLIR, and a big part our culture is to âBuild it Rightâ - this applies to every project we contribute to. As we contribute to and drive the evolution of MLIR, we have a vested interest in ensuring that steps are right for the project overall, and we collaborate with the MLIR community at large to gain consensus on our approach.

We took a step back to understand what we needed to solve this problem with large model tooling and listed out:

- Only allocate memory when necessary:Â We know it is more efficient to memory map large data (like weights) from disk, instead of copying data into mallocâd blocks.â

- No hashing or uniquing:Â Letâs not check equality of 2 gigabytes blobs of data; weights should be identified by name instead of being implicitly uniqueâd by content.â

- Enabling Inline Mutation:Â If there is only one user of the data, we should be able to quantize, transform and manipulate data in place instead of making a copy of it first.â

- Enable deallocation:Â The data we are working with is huge, and we need to deallocate it when the last reference to the data is destroyed.â

- Fast serialization:Â Whether JITing, searching optimization parameters, or just iterating locally, we cache IR for many reasons, and it should be fast.

None of these insights are particularly novel, but these are requirements that traditional compilers (e.g. for typical CPU programming languages) donât run into.

As a result of our design work, we added two extensions to MLIR that better support our use cases and are general to many other use cases throughout the community. We are actively contributing these back upstream to the MLIR community.

In part two of this series, we discuss how we separated IR and data, and the overall end-state impact. Hint, itâs huge. If you are an experienced Â developer in AI infrastructure or a MLIR practitioner, you should definitely consider joining Modular. If you like AI and enjoy using great tools, please subscribe to our newsletter.

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Engineering

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Eric Johnson

Product Lead

Product leader who has built and scaled AI applications and infrastructure. Eric led the TensorFlow API, Compiler, and Runtime teams at Google Brain and Core Systems, including the founding of TFRT and the productionization of JAX. He holds an MBA from Wharton and Computer Science MS from Penn and loves soccer, fitness, and the great outdoors.

eric@modular.com

================================================================================

URL: https://www.modular.com/blog/ais-compute-fragmentation-what-matrix-multiplication-teaches-us

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

March 23, 2023

# AIâs compute fragmentation: what matrix multiplication teaches us

Eric Johnson

Abdul Dakkak

Chad Jarvis

## Compute fragmentation is holding AI back

AI is powered by a virtuous circle of data, algorithms (âmodelsâ), and compute. Growth in one pushes needs in the others and can grossly affect the developer experience on aspects like usability and performance. Today, we have more data and more AI model research than ever before, but compute isnât scaling at the same speed due to â¦ well, physics.

If youâve followed the evolution of AI and hardware, youâve probably heard that Mooreâs law is ending. The rate of performance improvement in single-core processors is no longer doubling every 18 months, as has been true for the last 60 years. Beyond the physical limitations of continuing to make transistors smaller and smaller (e.g., excess power usage and heat due to current leakage), performance has become increasingly constrained by memory latency, which has grown much slower than processing speeds.

Yet, the need for more AI compute has continued to grow as models become larger and larger and more enterprise data is created and processed on the edge. As a result, squeezing performance from hardware has become one of the main focuses of the industry.

So how is compute fragmentation holding AI back? With traditional CPUs not scaling to meet the need for more compute, the only way forward has been to create parallel and domain-specific hardware that is less general, but that does a few AI-related things really well â like graphics processing units (GPUs), tensor processing units (TPUs), and other application-specific integrated circuits (ASICs). While these innovations have helped push the industry forward, enabling greater scale and more power-efficient processors in edge devices, the increased diversity of hardware has fragmented the industry and left AI developers struggling to:

- Develop software that fully leverages the hardwareâs capabilities and composes with others.

- Express parallel software algorithms on any one device.

- Scale that software across an ecosystem of many devices or even to heterogeneous systems.

Modular is focused on rebuilding the worldâs AI infrastructure from the ground up. In this blog series, weâll talk about how we are taking a fresh approach to solving the industryâs compute fragmentation problem. We will motivate the low-level challenges of building a truly unified solution by focusing on a single operation, matrix multiplication (âmatmulâ), a critical calculation in machine learning algorithms. To understand why it is hard, though, we will look under the hood of matmul and get into some of the nitty-gritty details of how it works. Donât say you werenât warned - this post is about to go deep!

## Matrix multiplication, and why it is so hard

Matrices are critical to machine learning systems as a simple way to represent data efficiently â such as input data (e.g., sets of pixels in an image) or the internal workings between the model layers. As a result, multiplying these matrices together makes up a large portion of the total computations in deep learning models. In fact, matmuls make up roughly 45-60% of the total runtime of many popular transformer models like BERT, CLIP, and even ChatGPT. Matmuls are also critical to computing the convolution operation that forms the foundation of most computer vision models, and makes up the backbone of many high-performance computing (âHPCâ) applications.

Given its importance, there has been extensive research on writing efficient matmul algorithms. Papers from the 60s, 70s, 80s, 90s, 2000s, to the present day exist that try to solve the problem with the hardware of that era.

But the conceptual matmul algorithm isnât what makes it difficult. Instead, the challenge is writing a matmul fast enough to achieve state-of-the-art performance across all the hardware, model, and data diversity in the AI industry. Â It is even harder to make it compose with all other AI operators.

### Hardware

Each device used to run AI models has different characteristics, including different memory hierarchies and different multiply and accumulate units (MAC).

For example, CPUs employ a hierarchy of memory from slow RAM to increasingly faster caches â Level-3, Level-2, Level-1, and CPU registers. The size of the memory is inversely proportional to its speed â for example, L1 cache access is typically on the order of single nanoseconds, whereas RAM access is on the order of 100 nanoseconds. To get the highest performance matmul, the algorithm itself has to be implemented to efficiently work with the different memory levels and sizes. Raw matrices are too big to fit into the registers or the fastest memory cache at one time, so the challenge is determining how to decompose them into the right sized blocks or âtilesâ that maximize usage of the fastest memory.

Moreover, the actual shapes of the units that handle the core matrix functionality differ across hardware. CPUs have traditionally been scalar machines, meaning they process instructions one step at a time, but all CPU vendors have added vector units (SIMD) over the last two decades. GPUs execute single operations across multiple threads (SIMT) to maximize the efficiency of highly parallel, repetitive operations like matmuls. And more specialized hardware takes this further by operating on 2D matrices. Google TPUs are the most well-known, but Apple and Intel have added their own matrix multiplication features called AMX. But while more advanced MAC units have led to improved performance, they have also created a greater need for flexible algorithms that work across scalar, vector, and matrix processors.

### Models

AI models are also quite diverse. While matmuls form the foundation of many models, the matrix sizes used in those matmuls can vary significantly. For example, models have different input shapes (like varying sequence lengths), different internal shapes (i.e., the matrices that are being multiplied together as part of the hidden layers of the model), and even different batch sizes (critical for training and inference efficiencies). As a result, matmuls come in hundreds of different shapes in production, which makes decomposing them into blocks that maximize memory efficiency challenging.

### Data

Lastly, your data can also be diverse. Most readers will be familiar with data diversity in terms of structured and unstructured data, but in this case, we are more concerned with data type (âdtypeâ). Data in AI models is usually of dtype FP32, but the industry is also adopting lower precision data types like Bfloat16, Int8, and the more exotic FP4 and Int4, to reduce model size and improve performance. Matmul algorithms, then, need to be able to operate on data of many different precisions, depending on the use case.

## Current state-of-the-art

So how are todayâs most state-of-the-art matmul algorithms actually implemented? Given its importance, matmul is usually one of the first algorithms that hardware vendors optimize, with many providing implementations through their libraries. Intel provides the MKL and OneDNN libraries, AMD provides AOCL and RocBLAS, ARM has performance libraries, Apple has Accelerate, and Nvidia provides CUBLAS.

Across the hardware libraries mentioned above, the current state of the art in terms of efficient implementation is to effectively write assembly code: this means micro-managing the hardware by giving it direct instructions at the lowest level that it understands without abstractions.

The main reason for this? Writing assembly produces the best performance for any one specific use-case because developers who are writing assembly can avoid the unpredictability of compilers, which translate higher-level languages like Python & C++ down to assembly, and can perform optimizations that are hard for the compiler to do because compilers must generalize. Importantly, they can leverage instructions and patterns that the compiler is unaware of because extending the compiler to support new hardware features takes time.

## Hand-written assembly kernels donât scale!

But does this actually solve the fragmentation issue for users? While writing in assembly maximizes performance for any individual example, itâs not portable, doesnât compose, doesnât scale, and isnât user-friendly. Think about it: how can a small number of specialized experts, who hand write and tune assembly code, possibly scale their work to all the different configurations while also incorporating their work into all the AI frameworks?! Itâs simply an impossible task.

### Portability

Because assembly is written with a hardware-specific interface called an instruction set architecture (ISA) it is not portable across different hardware platforms. In fact, assembly canât even deliver the best performance across multiple generations of chips from the same vendor!

Further, even if you have target hardware in mind when developing your model, there are still two big practical problems:

- In the cloud, you donât have control over the specific hardware it runs on. âBut Iâve selected an instance thatâs perfect for my modelâ you say. The truth is that instance types on some cloud providers like AWS donât guarantee a specific CPU type. For example, if you select a c5.4xlarge instance, you could get an older generation Intel SkyLake processor or a newer Cascade Lake processor. Assembly cannot adapt and give you the best performance for the specific chip your code runs on.

- Your product will continue to evolve rapidly, and you may want to move to a different hardware architecture altogether. Auguring in on one specific configuration will limit your flexibility to adapt as your model requirements change or when a new generation of hardware comes out.

### Scalability and composability

As we discussed earlier, there are many AI models, leading to hundreds of different matmul shapes. Using assembly-based libraries means selecting specific processor instructions that hardcode parameters like memory tiling sizes. These hard-coded assembly libraries can be tuned well for a specific tensor shape but require a different implementation for others. As a result, many existing kernel libraries swell to be gigabytes in size (e.g., MKL is 3.2GB and cuDNN can be up to 2.5GB). This becomes a problem when the size of these libraries impacts container build times, if you deploy to the edge where this is impractical, or if you want to deploy new innovations and research that these vendors havenât manually specialized yet.

Looking at the bigger picture, high-performance matmuls are indeed important for performance. But to get the best results, matmuls can be executed together with other operations such as elementwise, strided accesses, broadcasts, etc. Operator fusion provides significant performance improvements by reducing memory traffic - but the challenge is that there are thousands of AI operators. Furthermore, models use many permutations of different operations in combination, and (while some have tried!) it isnât practical to hand-fuse all the important combinations, particularly when research is driving AI forward so fast.

### User-friendliness

And finally, writing assembly is not user-friendly or good for cross-organization productivity. Assembly programming has limited features available in modern programming languages like parameterization and object-oriented programming and does not provide great tooling for debugging, code coverage, testing, etc. While the reality is that most researchers looking to write new operations are most comfortable in Python, it has well-known performance issues, so organizations end up having to hire expensive specialists to bridge the gap.

## Stay tuned

For AI to reach its true potential, compute fragmentation needs to be solved. AI software developers need to be able to seamlessly take full advantage of existing hardware and the next generation of hardware innovations when they become available. But as you can see, solving this problem is not easy. Diversity in hardware, models, and data means that every existing solution on the market is only just a âpoint solutionâ to a much broader problem.

Modular is solving this problem - stay tuned for part 2 of the blog, in which we outline our approach and show the revolutionary benefits. And if you are excited about solving some of the most challenging and complex problems, go to modular.com/careers and apply!

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Engineering

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Eric Johnson

Product Lead

Product leader who has built and scaled AI applications and infrastructure. Eric led the TensorFlow API, Compiler, and Runtime teams at Google Brain and Core Systems, including the founding of TFRT and the productionization of JAX. He holds an MBA from Wharton and Computer Science MS from Penn and loves soccer, fitness, and the great outdoors.

eric@modular.com

Abdul Dakkak

AI Compiler Engineer

Expert in machine learning, compilers, programming languages, and accelerated computing. Before Modular, Abdul led the development of AI compilers for GPUs at Microsoft Research and the Mathematica Compiler at Wolfram Research. Abdul has developed open-source tools for accelerating real-world applications to optimize their performance across the hardware and software stack.



Chad Jarvis

AI Performance Engineer

Chad has a strong background in low-level code optimization, parallel programming, and high performance computing. He has worked for more than 17 years in research and engineering in Europe and North America. He holds a PhD in particle physics and is one of the authors of the Higgs Boson discovery. Chad has a passion and focus for understanding things at a fundamental level. Prior to joining Modular he worked at Graphcore working closely with the hardware engineers to implement and optimize custom features of the GC hardware, and before that Simula Research Laboratory among many other distinguished research facilities. He enjoys spending his spare time with his family, travelling, collecting rare coins, and eating excellent food.



================================================================================

URL: https://www.modular.com/team/chad-jarvis

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/team/hengjie-wang

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/team/ian-tramble

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?author=Chad+Jarvis

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?author=Hengjie+Wang

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?author=Ian+Tramble

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog/accelerating-ai-model-serving-with-the-modular-ai-engine

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

June 1, 2023

# Accelerating AI model serving with the Modular AI Engine

Alexandr Nikitin

Eric Johnson

## AI Model Serving is more than an Engine

A few weeks ago, we announced the worldâs fastest unified AI inference engine. The Modular AI Engine provides significant usability, portability, and performance gains for the leading AI frameworks â PyTorch and TensorFlow â and delivers world-leading execution performance for all cloud-available CPU architectures. Critically, it is a drop-in replacement to how you deploy AI today, including support for all AI operators, ensuring your workloads seamlessly capture these benefits out of the box.

â

However, as we previously discussed, running inference on a model is only part of the deployment story. Deploying AI models into production at scale requires significantly more software infrastructure and system design, as seen in the figure below as just one example. In addition to leveraging a model engine that supports multiple frameworks and hardware backends, the serving layer must account for other requirements such as high throughput (via techniques such as model concurrency and dynamic batching) and low latency, while providing ease of use, reliability, and scalability.

â

Given the complex nature of serving infrastructure, optimizing the performance of an AI application in production is not only about optimizing the backend runtime engine. Improving the engine's performance leads to bottlenecks in other parts of the stack, so tuning each stage of the end-to-end serving infrastructure, including model loading, batching, etc., is critical.

## Integration into popular serving solutions

At Modular, we care about maximizing AI developer velocity by meeting customers where they are today. So in order to make deployment of the Modular AI Engine as seamless as possible, we have integrated it into NVIDIAâs Triton Inference Server and TensorFlow Serving. Both are open-source serving solutions that many of our customers are using today, and they provide useful features that simplify inference deployment, such as out-of-the-box support for metrics, dynamic batching, concurrency, and more. In the case of Triton, we used the backend API to implement a Modular AI Engine backend, as seen in the image below. With the Triton serving API remaining the same, we provide a drop-in option for customers to transparently capture all of the Modular AI Engine's benefits. Weâve worked hard to make it incredibly simple to roll out the worldâs fastest AI inference engine.

â

## Performance benefits of the Modular AI Engine for serving

To showcase the benefits of the Modular AI Engine, we analyze the performance of the Engine integrated into the Triton Inference Server on various hardware backends â Intel Skylake (c5.4xlarge on EC2), AMD EPYC (c5a.4xlarge), and AWS Graviton2 (c6g.4xlarge). We test the performance on a binary text classification problem using BERT-base, a popular transformer language model from HuggingFace. We use a sequence length of 128 and analyze the effects of increasing concurrency and enabling server-side dynamic batching on the Modular backend to simulate a production deployment of the model. And we compare the results of the Modular AI Engine to the out-of-the-box performance of TensorFlow and PyTorch backends. We also did not change any default settings (e.g., dynamic batch size) on Triton to keep this as consistent as possible with how our customers use it in production.

### Throughput

The first set of charts below show throughput (queries per second) as we scale the number of concurrent threads on the serving system. Starting with our results on the AWS Graviton 2, we see that Modular is able to achieve 2.3x better throughput as compared to TensorFlow and 1.5x-1.7x as compared to the PyTorch 2.0 backend.

We see roughly the same improvement on AMD as well, with Modular 2.3x better than TensorFlow and 1.5x-1.7x as compared to PyTorch 2.0. Also interesting to note is that with dynamic batching enabled, we can see an improved scaling effect on the throughput as we increase the number of concurrent requests (or threads) being serviced. Not surprisingly, without dynamic batching, as the requests spend more time in the queue, the throughput stays constant.

On the Intel Skylake system, Modular has 3.6x better throughput on TensorFlow and 1.2x better throughput than PyTorch 2.0. Modular achieves a peak throughput at 42 QPS on the Intel Skylake system without changing hardware or numerics, or performing other âtricksâ.

### Latency

Since production deployments typically operate under a latency budget, we also measure the latency of the Modular AI Engine compared to both TensorFlow and PyTorch 2.0. On the Graviton2 system, at the peak throughput, Modular is able to achieve 2.3x lower latency compared to TensorFlow and 1.5x-1.7x lower latency compared to PyTorch 2.0 as the number of concurrent requests is increased.

Similarly, on AMD, Modular is 2.3x lower latency than TensorFlow and 1.5x-1.7x lower latency than PyTorch 2.0.

On the Intel Skylake system, Modular has 2x lower latency compared to TensorFlow and 1.2x lower latency compared to PyTorch 2.0. Note that the PyTorch implementation is using the optimized Intel OneDNN Graph BERT implementation, and as you can see, our ability to scale across multiple architectures while providing unmatched latency performance makes the Modular AI Engine world leading.

Further, we are showcasing significant wins against a very common and highly tuned model, and the generality of our stack enables enormous improvements across many other models. You can combine all these benefits for a truly industry defining solution â much greater flexibility, usability (e.g. full support for dynamic sequence lengths), integration with Mojo and of course performance portability across hardware architectures.

## Whatâs Next?

You can check out the complete NVIDIA Triton serving results using Modularâs AI Engine on our Performance Dashboard. However, this is only the beginning. Modular is working on an accelerated AI compute platform that can serve performance-sensitive AI models at scale, and we have many more optimizations to come as we build out our platform. Stay tuned! In the meantime, visit www.modular.com to learn more and sign up for our newsletter to get product updates and notifications when new results are available on our performance dashboard.

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Product

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Alexandr Nikitin

AI Cloud Engineer

Alex is a software engineer with over 15 years of experience who is passionate about building efficient, fault-tolerant, and high-performance systems. Prior to joining Modular, he led the Einstein Platform ML Serving solution at Salesforce, which scaled to support hundreds of thousands of ML models and prior to that build extremely high performance ads systems. In his free time, Alex enjoys mountaineering, climbing, and trail running.



Eric Johnson

Product Lead

Product leader who has built and scaled AI applications and infrastructure. Eric led the TensorFlow API, Compiler, and Runtime teams at Google Brain and Core Systems, including the founding of TFRT and the productionization of JAX. He holds an MBA from Wharton and Computer Science MS from Penn and loves soccer, fitness, and the great outdoors.

eric@modular.com

================================================================================

URL: https://www.modular.com/blog/do-llms-eliminate-the-need-for-programming-languages

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

June 8, 2023

# Do LLMs eliminate the need for programming languages?

Chris Lattner

## Introduction

Weâre very excited about the positive reception of Mojo since its launch as well as the community of people building around it. Given new Large Language Model (LLM) powered developer tools like Copilot and Ghostwriter, many developers are wondering about the future of programming â do programming languages still matter when AI writes the code?

This is a great question! It cuts to the heart of developer workflows, allows us to reflect on the core purpose of programming tools more broadly, and encourages us to share our perspective on where coding technologies are going over the long term. First, letâs explore what a programming language is for across three critical dimensions.

## The âHuman to computerâ specification

One of my early passions when learning how to code was understanding how the whole stack worked, and I got involved in programming language design and implementation as a student. As part of that, my first viewpoint was that a programming language is an abstraction a human uses to express intent to the computer about what a piece of code is supposed to do. I learned that source code is a ârecipeâ that a compiler or interpreter translates into something the computer can understand.

With this viewpoint, it makes sense for a programming language to be focused on exposing the capabilities of the machine in an accessible way that many humans can understand. A language should be designed to be written with an unambiguous interpretation that allows precise specification of an algorithm, or design, and being concise allows smart programmers to get much done quickly. This is one of the reasons that some âbigâ languages targeted to expert developers (like C++) accumulate syntactic sugar and core language features that enable the efficient expression of important things.

While this viewpoint is valid, as I gained more experience, I realized that this is only one part of what programming languages do.

## The âHuman to humanâ specification

I learned very quickly that most interesting projects are built by teams of people and grow to the size where it is hard to fit all the code in your head. When this happens, new dynamics enter the software development realm â design discussions, code reviews, and 3rd-party library/package integrations. The best and most fulfilling software developments I have ever participated in have always been with groups of talented and dedicated people.

In this context, one quickly realizes that the purpose of a programming language grows to be an abstraction for a human to express intent to another human about the behavior of a program. This still requires unambiguous specification but also shifts the goalposts â a language should be designed to be read, not just written by other humans. Computers are very tolerant and understanding (particularly with the rise of LLMs), so many of us benefit from clear design patterns and easy-to-understand code. Most code ends up being written once, but read and iterated on many times by many people.

The consequence is that overly clever syntactic sugar actually starts to cut against the core goals of the language. Specialized syntax and infrequently used features can make it difficult to understand for those who didnât write it. While LLMs and other tools can help decode or explain overly complicated code, keeping a single, readable source of truth is ideal.

## The âComputer to Humanâ specification

With this lens, LLM-based code generation tools act like a new team member contributing, reading, and manipulating code in a project. There are many different examples with varied features and capabilities, e.g., a human prompt to generate code, an AI expert that reviews your code and offers suggestions to improve it, tools to automate the generation of unit tests, and other new capabilities are coming out all the time. The defining feature of these tools is that the computer generates source code that gets integrated into a product.

While these capabilities are incredible, at least in the near future, these code-generation tools donât displace the existing functions of a programming language. Indeed, one of the major concerns about language models today is trust â they can give strikingly amazing results in some cases but are often subtly wrong in others, and some are non-deterministic. As such, it becomes even more critical to design a language to be read, not just written by other humans, so we humans can review and approve generated code.Â

Letâs consider some examples: if you prompted an LLM to build you a mobile app that processes online purchases, would you ship that without reviewing the source code to make sure it does billing right? Or, more extreme yet, would you want to send humans to the moon with code written by an LLM? This begs the ultimate question for developers â what margin of error and cost are we willing to accept? The unreliability of LLMs today means that, as code owners, we need to know if the prompt generated something with the right behavior - what does the generated code actually do? This is also why an LLM that directly generates low-level machine code isnât interesting for general use cases â few people want to read, review, and validate machine code.

As we look towards the future, we hope that LLMs will augment the developer experience and become more reliable and trustworthy over time. But even if this proves true, LLMs still wonât replace the need for programming languages. LLMs are likely to become a critical extension of the highly productive (âlazyâ) developer â a substantial level-up vs copy/pasting from online references. Further, while LLMs will very likely automate away the boilerplate and repetitive parts of programming, there will always be use cases that require the human touch.

While no one knows the future, we think humans will need to be in the loop for quite some time for many applications â particularly where the error margins are low, and resulting costs are high.

## What's the ideal programming language for LLMs to output?

Those deep in software development find themselves surrounded by a zoo of different languages that aim to solve problems in various niches. You might have encountered Python for AI and data science, C and C++ for low-level programming, Javascript or Typescript for the web, Swift and Kotlin when building a mobile app, and CUDA for accelerator programming. These are all valuable languages, but given that LLMs reduce the need to care about how writable a syntax is â what qualities of a programming language matter in this new age?

We believe there are three fundamental aspects of a programming language that would make it particularly useful as we head towards an AI-assisted world â its usability and scalability to many domains, the amount of training data that exists, and a rich and vibrant ecosystem. Letâs take each in turn:

- The first most critical part of a language is the usability and scalability of the language implementation. The best language for an LLM is one that is highly usable and easy to read for humans, but whose implementation can scale to many different use cases and applications. Unfortunately, many language implementations include design decisions that preclude certain applications. For example, mark/sweep garbage collection isnât ideal for low-level system software and accelerator programming, Python and other interpreted languages arenât ideal when performance, parallelism, and threading are required, and JVM or .NET-based languages arenât ideal for use cases that need small and low-dependence binaries.Â

- To train an LLM that is capable of producing high-quality programs across many different use cases and applications, we need an expansive corpus of training data that seeds the model. An LLM will work much better on a popular and established language like Python which has a large and diverse set of open examples, than a niche or novel language that has no existing code to train on.

- Lastly, we believe that a LLM needs a rich and vibrant ecosystem surrounding it. Even for existing LLM-based solutions, rich communities have already developed prompting libraries, tooling, and expertise enabling next-generation ecosystems to form. With this viewpoint, a language should be designed to unlock a massive community of developers â however we choose to define a developer in this new world, from traditional programming to instruction prompting and beyond.

When we look at the vast number of existing programming languages, we see many points in the space, but they all provide tradeoffs optimized for different niches. How can we move the state-of-the-art forward? In our view, Mojo is a powerful contender to grow into an ideal language for LLMs, as it meets all three fundamental aspects above.

## Our approach with Mojo

Weâve learned a lot from building other compiler and programming language systems (e.g., Clang/C++, Swift, etc) over the last 20+ years. From that experience, we are building Mojo to:

- Be a fully compatible superset of Python, benefiting from its easy to read and understandable syntax and enabling its large community of developers to already know how to write Mojo!

- Support system programming features and hardware accelerators that extend the performance and reach of Python into new domains as we move into a new parallel-computing world.

- Be fully integrated with the existing Python ecosystem, extending and benefiting from all of the existing packages. We will also build seamless C and C++ interoperability to lift (and benefit from) work in those communities over time.

- Provide a new high-performance heterogeneous compiler and runtime implementation that benefits from state-of-the-art techniques.

As a consequence, we believe Mojo fits the perfect sweet spot for LLMs to generate and output highly scalable code, because it combines the human readability and usability of Python, but extends it with powerful lower-level systems features that enable it to scale across more hardware and drive the next set of the worldâs applications and use cases.

We think LLMs will continue to unlock creativity and productivity across many languages, but we also believe Mojo will be well prepared to lift collaborative software development to the next level and bring programming into new frontiers.

Mojo is still early â we unveiled its 0.1 release last month âÂ  but we are in this for the long term and have clear goals. If you are interested in being a part of Mojo early on, please join our community or apply to help us build it!

## Next blog post:

- ð¨NEWð¥POPULAREngineeringAnnouncing stack-pr: an open source tool for managing stacked PRs on GitHubJuly 23, 2024

- Join our communityBuild the future of AI with us...and +125k developers

- What is MAX?Learn more about our FREE framework for GenAI

ð¨

NEW

ð¥

POPULAR

Engineering

Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

July 23, 2024

Join our community

Build the future of AI with us...and +125k developers

What is MAX?

Learn more about our FREE framework for GenAI

View All Blogs

Industry

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

Chris Lattner

Co-Founder & CEO

Distinguished Leader who founded and scaled critical infrastructure including LLVM, Clang, MLIR, Cloud TPUs and the Swift programming language. Chris built AI and core systems at multiple world leading technology companies including Apple, Google, SiFive and Tesla.

clattner@modular.com

================================================================================

URL: https://www.modular.com/blog-all?topic=Community

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/team/mohamed-mabrouk

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?author=Mohamed+Mabrouk

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/values

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/team/alexandr-nikitin

YOUÂ LANDEDÂ INÂ THEÂ WRONGÂ SPOT

# Page not found ð¥º

The page you are looking for doesn't exist or has been moved.

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?author=Alexandr+Nikitin

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================

URL: https://www.modular.com/blog-all?topic=Industry

PRODUCT

- MAXâ¨

MAXâ¨

Language

- Mojoð¥

Mojoð¥

Quick Start

- Install

- Run LLMs

- Pricing

Install

Run LLMs

Pricing

Take control of your AI

Author:

PRODUCT

- Documentation

- Tutorials

- Blog

Documentation

Tutorials

Blog

Build

- â¨ Prebuilt AI Pipelines

- â¨ MAX Examples

- ð¥ Mojo Examples

- ð¥ Mojo Playground

â¨ Prebuilt AI Pipelines

â¨ MAX Examples

ð¥ Mojo Examples

ð¥ Mojo Playground

Read

- Join Discord

- MAX Changelog

- Newsletter

Join Discord

MAX Changelog

Newsletter

Bring your own fine-tuned model to MAX pipelines

Author:

MODULAR

- About

- Culture

- Careers

About

Culture

Careers

Connect

- Community

- Contact Us

- 

- 

- 

- 

- 

- 

Community

Contact Us

- 

- 

- 

- 

- 

ModCon 2023

Author:

# All ArticlesÂ Â (X)

Topics

Topic

Authors

Authors

ð¨

NEW

Engineering

### Announcing stack-pr: an open source tool for managing stacked PRs on GitHub

We are pleased to announce the release of a new tool aimed at simplifying the management of stacked pull requests (PRs) on GitHub - stack-pr. This tool is still in its early development days, but we are excited to share it with the community and welcome your contributions.

July 23, 2024

/

Mikhail Zolotukhin

,Â

ð¨

NEW

ð¥

Popular

Engineering

### Debugging in Mojoð¥

Developer tooling is a big priority for Mojo and MAX, we want to vastly improve the debugging experience compared to the traditional Python, C++, and CUDA stack. Machine learning often requires inspecting the state of a program after a long running process, requiring more control than what "print debugging" gives you. Over time this tooling will extend to GPUs, allowing you to step through CPU code into GPU calls with the same developer experience.

July 16, 2024

/

Jack Clayton

,Â

Walter Erquinigo

,Â

ð¨

NEW

Product

### Bring your own PyTorch model

The adoption of AI by enterprises has surged significantly over the last couple years, particularly with the advent of Generative AI (GenAI) and Large Language Models (LLMs). Most enterprises start by prototyping and building proof-of-concept products (POCs), using all-in-one API endpoints provided by big tech companies like OpenAI and Google, among others. However, as these companies transition to full-scale production, many are looking for ways to control their AI infrastructure. This requires the ability to effectively manage and deploy PyTorch.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

Product

### Take control of your AI

In todayâs rapidly evolving technology landscape, adopting and rolling out AI to enhance your enterprise is critical to improving your organizationâs productivity and ensuring that you are delivering a world-class product and service experience to your customers. AI is without question, the single most important technological revolution of our timeârepresenting a new technology super-cycle that your enterprise cannot be left behind on.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Product

### Develop locally, deploy globally

The recent surge in AI application development can be attributed to several factors: (1) advancements in machine learning algorithms that unlock previously intractable use cases, (2) the exponential growth in computational power enabling the training of ever-more complex models, and (3) the ubiquitous availability of vast datasets required to fuel these algorithms. However, as AI projects become increasingly pervasive, effective development paradigms, like those commonly found in traditional software development, remain elusive.

July 9, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### A brief guide to the Mojo n-body example

Since August 2023, the Mojo repository has included a small benchmark example titled nbody.mojo. This code is based on an example from The Computer Language Benchmarks Game, a site that benchmarks implementations of different algorithms in popular programming languages.

July 3, 2024

/

Chris Hoge

,Â

ð¨

NEW

Developer

### What's new in MAX 24.4? MAX on macOS, fast local Llama3, native quantization and GGUF support

In our recent MAX 24.4 release, we announced the availability of MAX on MacOS and MAX Pipelines with native support for local Generative AI models such as Llama3. Together, these innovations establish a new industry standard paradigm, enabling developers to leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance.Â

June 25, 2024

/

Ehsan M. Kermani

,Â

ð¨

NEW

Developer

### Whatâs new in Mojo 24.4? Improved collections, new traits, os module features and core language enhancements

Mojo 24.4 is now available for download, and this release includes several core language and standard library enhancements. In this blog post, weâll dive deep into many of these features using code examples. One of the biggest highlights of this release is that we received 214 pull requests from 18 community contributors for new product features, bug fixes, documentation enhancements, and code refactoring. These contributions resulted in 30 net new features in the standard library, accounting for 11% of all improvements in this release. Weâre incredibly proud of the momentum weâre seeing with community contributions, and it goes without saying â you are the real star of this release. On behalf of the entire Mojo team, weâd like to thank you for all your contributions to making Mojo awesome!

June 17, 2024

/

Shashank Prasanna

,Â

ð¨

NEW

Product

### MAX 24.4 - Introducing quantization APIs and MAX on macOS

Today, we're thrilled to announce the release of MAX 24.4, which introduces a powerful new quantization API for MAX Graphs and extends MAXâs reach to macOS. Together, these unlock a new industry standard paradigm where developers can leverage a single toolchain to build Generative AI pipelines locally and seamlessly deploy them to the cloud, all with industry-leading performance. Leveraging the Quantization API reduces the latency and memory cost of Generative AI pipelines by up to 8x on desktop architectures like macOS, and up to 7x on cloud CPU architectures like Intel and Graviton, without requiring developers to rewrite models or update any application code.

June 7, 2024

/

Modular Team

,Â

ð¨

NEW

ð¥

Popular

Developer

### Deep dive into ownership in Mojo

This post blog is the second part of the series of ownership in Mojo. Please make sure to check out the first part, What Ownership is Really About: A Mental Model Approach, as we will build on concepts developed there.Â This post serves as accompanying material for the deep dive on ownership by our CEO, Chris Lattner. Be sure to watch the video as well, which covers how ownership is implemented in Mojo's compiler, providing further insights and technical details.

June 4, 2024

/

Ehsan M. Kermani

,Â

ð¤

No results for this query

Reset all filters

- Join our communityBuild the future of AI with us...and +125k developers

- Browse examplesRun a project locally in just 2min

- What is MAX?Learn more about our FREE framework for GenAI

Join our community

Build the future of AI with us...and +125k developers

Browse examples

Run a project locally in just 2min

What is MAX?

Learn more about our FREE framework for GenAI

- ProductMAXMojoð¥Docs

- MAX

- Mojoð¥

- Docs

- QUICKÂ STARTGet Started with MAXRun AI under 3 minsMAX PricingDevelop local, Deploy global

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

- DEVELOPERSMAX PipelinesInstall MAXMAXÂ APIÂ ReferenceMAXÂ ChangelogMojo ð¥ Playground

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

- ENTERPRISESMAX for EnterprisesPricingTalk to Sales

- MAX for Enterprises

- Pricing

- Talk to Sales

- SUPPORTBlogCommunityModverse WeeklyAIÂ ResourcesReport Issue

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

- CompanyAbout UsCultureCareersContact

- About Us

- Culture

- Careers

- Contact

Product

- MAX

- Mojoð¥

- Docs

MAX

Mojoð¥

Docs

QUICKÂ START

- Get Started with MAX

- Run AI under 3 mins

- MAX Pricing

- Develop local, Deploy global

Get Started with MAX

Run AI under 3 mins

MAX Pricing

Develop local, Deploy global

DEVELOPERS

- MAX Pipelines

- Install MAX

- MAXÂ APIÂ Reference

- MAXÂ Changelog

- Mojo ð¥ Playground

MAX Pipelines

Install MAX

MAXÂ APIÂ Reference

MAXÂ Changelog

Mojo ð¥ Playground

ENTERPRISES

- MAX for Enterprises

- Pricing

- Talk to Sales

MAX for Enterprises

Pricing

Talk to Sales

SUPPORT

- Blog

- Community

- Modverse Weekly

- AIÂ Resources

- Report Issue

Blog

Community

Modverse Weekly

AIÂ Resources

Report Issue

Company

- About Us

- Culture

- Careers

- Contact

About Us

Culture

Careers

Contact

- 

- 

- 

- 

- 

Copyright Â© 2024 Modular Inc

Terms, Privacy & Acceptable Use

================================================================================


--- LICENSE ---

MIT License

Copyright (c) 2024 Maxim Zaks

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


--- README.MD ---

# MOJO-Sort

Implementation of different sorting algorithms in Mojo

--- benchmark.mojo ---

from csv import CsvBuilder
from random import random_ui64, random_si64, random_float64
from memory.unsafe import bitcast
from my_utils import print_v
from time import now
from quick_sort import quick_sort
from radix_sorting import radix_sort, radix_sort11, radix_sort13, radix_sort16
from selection_sort import selection_sort
from insertion_sort import insertion_sort
from count_sort import counting_sort
from tim_sort import tim_sort, parallel_tim_sort

fn random_vec[D: DType](size: Int, max: Int = 3000) -> List[SIMD[D, 1]]:
    var result = List[SIMD[D, 1]](size)
    for _ in range(size):
        @parameter
        if D == DType.int8 or D == DType.int16 or D == DType.int32 or D == DType.int64:
            result.append(random_si64(0, max).cast[D]())
        elif D == DType.float16 or D == DType.float32 or D == DType.float64:
            result.append(random_float64(0, max).cast[D]())
        else:
            result.append(random_ui64(0, max).cast[D]())
    return result

fn assert_sorted[D: DType](vector: List[SIMD[D, 1]], inout builder: CsvBuilder):
    for i in range(1, len(vector)):
        if vector[i] < vector[i - 1]:
            builder.push(False, False)
            return
    builder.push(True, False)

fn benchmark[D: DType, func: fn(inout List[SIMD[D, 1]]) -> None](
    name: StringLiteral, size: Int, inout builder: CsvBuilder, max: Int = 3000,
):
    var v = random_vec[D](size, max)
    var v1 = v
    var min_duration = Int64.MAX
    for _ in range(10):
        v1 = v
        var tik = now()
        func(v1)
        var tok = now()
        min_duration = min(min_duration, tok - tik)
    builder.push(name, False)
    builder.push(str(D), False)
    builder.push(size, False)
    builder.push(max, False)
    builder.push(min_duration, False)
    builder.push(min_duration // size, False)
    assert_sorted[D](v1, builder)

fn std_sort[D: DType](inout vector: List[SIMD[D, 1]]):
    sort[D](vector)

fn main():
    var builder = CsvBuilder("Algorithm name", "DType", "List size", "Max value", "Min total duration", "Min duration per element", "Sorted")
    benchmark[DType.uint8, selection_sort[DType.uint8]]("Selection sort", 5, builder)
    benchmark[DType.uint8, insertion_sort[DType.uint8]]("Insertion sort", 5, builder)
    benchmark[DType.uint8, std_sort[DType.uint8]]("Std sort", 5, builder)
    benchmark[DType.uint8, quick_sort[DType.uint8]]("Quick sort", 5, builder)
    benchmark[DType.uint8, tim_sort[DType.uint8]]("Tim sort", 5, builder)
    benchmark[DType.uint8, counting_sort[DType.uint8]]("Counting sort", 5, builder)
    benchmark[DType.uint8, radix_sort[DType.uint8]]("Radix sort", 5, builder)

    benchmark[DType.uint8, selection_sort[DType.uint8]]("Selection sort", 20, builder)
    benchmark[DType.uint8, insertion_sort[DType.uint8]]("Insertion sort", 20, builder)
    benchmark[DType.uint8, std_sort[DType.uint8]]("Std sort", 20, builder)
    benchmark[DType.uint8, quick_sort[DType.uint8]]("Quick sort", 20, builder)
    benchmark[DType.uint8, tim_sort[DType.uint8]]("Tim sort", 20, builder)
    benchmark[DType.uint8, counting_sort[DType.uint8]]("Counting sort", 20, builder)
    benchmark[DType.uint8, radix_sort[DType.uint8]]("Radix sort", 20, builder)
    
    benchmark[DType.uint8, selection_sort[DType.uint8]]("Selection sort", 50, builder)
    benchmark[DType.uint8, insertion_sort[DType.uint8]]("Insertion sort", 50, builder)
    benchmark[DType.uint8, std_sort[DType.uint8]]("Std sort", 50, builder)
    benchmark[DType.uint8, quick_sort[DType.uint8]]("Quick sort", 50, builder)
    benchmark[DType.uint8, tim_sort[DType.uint8]]("Tim sort", 50, builder)
    benchmark[DType.uint8, counting_sort[DType.uint8]]("Counting sort", 50, builder)
    benchmark[DType.uint8, radix_sort[DType.uint8]]("Radix sort", 50, builder)

    benchmark[DType.uint8, selection_sort[DType.uint8]]("Selection sort", 300, builder)
    benchmark[DType.uint8, insertion_sort[DType.uint8]]("Insertion sort", 300, builder)
    benchmark[DType.uint8, std_sort[DType.uint8]]("Std sort", 300, builder)
    benchmark[DType.uint8, quick_sort[DType.uint8]]("Quick sort", 300, builder)
    benchmark[DType.uint8, tim_sort[DType.uint8]]("Tim sort", 300, builder)
    benchmark[DType.uint8, counting_sort[DType.uint8]]("Counting sort", 300, builder)
    benchmark[DType.uint8, radix_sort[DType.uint8]]("Radix sort", 300, builder)

    benchmark[DType.int8, selection_sort[DType.int8]]("Selection sort", 300, builder)
    benchmark[DType.int8, insertion_sort[DType.int8]]("Insertion sort", 300, builder)
    benchmark[DType.int8, std_sort[DType.int8]]("Std sort", 300, builder)
    benchmark[DType.int8, quick_sort[DType.int8]]("Quick sort", 300, builder)
    benchmark[DType.int8, tim_sort[DType.int8]]("Tim sort", 300, builder)
    benchmark[DType.int8, parallel_tim_sort[DType.int8]]("Parallel Tim sort", 300, builder)
    benchmark[DType.int8, radix_sort[DType.int8]]("Radix sort", 300, builder)

    benchmark[DType.uint16, selection_sort[DType.uint16]]("Selection sort", 3000, builder)
    benchmark[DType.uint16, insertion_sort[DType.uint16]]("Insertion sort", 3000, builder)
    benchmark[DType.uint16, std_sort[DType.uint16]]("Std sort", 3000, builder)
    benchmark[DType.uint16, quick_sort[DType.uint16]]("Quick sort", 3000, builder)
    benchmark[DType.uint16, tim_sort[DType.uint16]]("Tim sort", 3000, builder)
    benchmark[DType.uint16, parallel_tim_sort[DType.uint16]]("Parallel Tim sort", 3000, builder)
    benchmark[DType.uint16, counting_sort[DType.uint16]]("Counting sort", 3000, builder)
    benchmark[DType.uint16, radix_sort[DType.uint16]]("Radix sort", 3000, builder)

    benchmark[DType.int16, selection_sort[DType.int16]]("Selection sort", 3000, builder)
    benchmark[DType.int16, insertion_sort[DType.int16]]("Insertion sort", 3000, builder)
    benchmark[DType.int16, std_sort[DType.int16]]("Std sort", 3000, builder)
    benchmark[DType.int16, quick_sort[DType.int16]]("Quick sort", 3000, builder)
    benchmark[DType.int16, tim_sort[DType.int16]]("Tim sort", 3000, builder)
    benchmark[DType.int16, parallel_tim_sort[DType.int16]]("Parallel Tim sort", 3000, builder)
    benchmark[DType.int16, radix_sort[DType.int16]]("Radix sort", 3000, builder)

    benchmark[DType.float16, selection_sort[DType.float16]]("Selection sort", 3000, builder)
    benchmark[DType.float16, insertion_sort[DType.float16]]("Insertion sort", 3000, builder)
    benchmark[DType.float16, std_sort[DType.float16]]("Std sort", 3000, builder)
    benchmark[DType.float16, quick_sort[DType.float16]]("Quick sort", 3000, builder)
    benchmark[DType.float16, tim_sort[DType.float16]]("Tim sort", 3000, builder)
    benchmark[DType.float16, parallel_tim_sort[DType.float16]]("Parallel Tim sort", 3000, builder)
    benchmark[DType.float16, radix_sort[DType.float16]]("Radix sort", 3000, builder)

    benchmark[DType.uint32, std_sort[DType.uint32]]("Std sort", 300_000, builder, 2_000_000_000)
    benchmark[DType.uint32, quick_sort[DType.uint32]]("Quick sort", 300_000, builder, 2_000_000_000)
    benchmark[DType.uint32, tim_sort[DType.uint32]]("Tim sort", 300_000, builder, 2_000_000_000)
    benchmark[DType.uint32, parallel_tim_sort[DType.uint32]]("Parallel Tim sort", 300_000, builder, 2_000_000_000)
    benchmark[DType.uint32, radix_sort[DType.uint32]]("Radix sort", 300_000, builder, 2_000_000_000)
    benchmark[DType.uint32, radix_sort11[DType.uint32]]("Radix sort 11", 300_000, builder, 2_000_000_000)

    benchmark[DType.int32, std_sort[DType.int32]]("Std sort", 300_000, builder, 2_000_000_000)
    benchmark[DType.int32, quick_sort[DType.int32]]("Quick sort", 300_000, builder, 2_000_000_000)
    benchmark[DType.int32, tim_sort[DType.int32]]("Tim sort", 300_000, builder, 2_000_000_000)
    benchmark[DType.int32, parallel_tim_sort[DType.int32]]("Parallel Tim sort", 300_000, builder, 2_000_000_000)
    benchmark[DType.int32, radix_sort[DType.int32]]("Radix sort", 300_000, builder, 2_000_000_000)
    benchmark[DType.int32, radix_sort11[DType.int32]]("Radix sort 11", 300_000, builder, 2_000_000_000)

    benchmark[DType.float32, std_sort[DType.float32]]("Std sort", 5_000_000, builder, 2_000_000_000)
    benchmark[DType.float32, quick_sort[DType.float32]]("Quick sort", 5_000_000, builder, 2_000_000_000)
    benchmark[DType.float32, tim_sort[DType.float32]]("Tim sort", 5_000_000, builder, 2_000_000_000)
    benchmark[DType.float32, parallel_tim_sort[DType.float32]]("Parallel Tim sort", 5_000_000, builder, 2_000_000_000)
    benchmark[DType.float32, radix_sort[DType.float32]]("Radix sort", 5_000_000, builder, 2_000_000_000)
    benchmark[DType.float32, radix_sort11[DType.float32]]("Radix sort 11", 5_000_000, builder, 2_000_000_000)

    benchmark[DType.uint64, std_sort[DType.uint64]]("Std sort", 3_000_000, builder, 200_000_000_000)
    benchmark[DType.uint64, quick_sort[DType.uint64]]("Quick sort", 3_000_000, builder, 200_000_000_000)
    benchmark[DType.uint64, tim_sort[DType.uint64]]("Tim sort", 3_000_000, builder, 200_000_000_000)
    benchmark[DType.uint64, parallel_tim_sort[DType.uint64]]("Parallel Tim sort", 3_000_000, builder, 200_000_000_000)
    benchmark[DType.uint64, radix_sort[DType.uint64]]("Radix sort", 3_000_000, builder, 200_000_000_000)
    benchmark[DType.uint64, radix_sort13[DType.uint64]]("Radix sort 13", 3_000_000, builder, 200_000_000_000)

    benchmark[DType.int64, std_sort[DType.int64]]("Std sort", 3_000_000, builder, 200_000_000_000)
    benchmark[DType.int64, quick_sort[DType.int64]]("Quick sort", 3_000_000, builder, 200_000_000_000)
    benchmark[DType.int64, tim_sort[DType.int64]]("Tim sort", 3_000_000, builder, 200_000_000_000)
    benchmark[DType.int64, parallel_tim_sort[DType.int64]]("Parallel Tim sort", 3_000_000, builder, 200_000_000_000)
    benchmark[DType.int64, radix_sort[DType.int64]]("Radix sort", 3_000_000, builder, 200_000_000_000)
    benchmark[DType.int64, radix_sort13[DType.int64]]("Radix sort 13", 3_000_000, builder, 200_000_000_000)

    benchmark[DType.float64, std_sort[DType.float64]]("Std sort", 3_000_000, builder, 200_000_000_000)
    benchmark[DType.float64, quick_sort[DType.float64]]("Quick sort", 3_000_000, builder, 200_000_000_000)
    benchmark[DType.float64, tim_sort[DType.float64]]("Tim sort", 3_000_000, builder, 200_000_000_000)
    benchmark[DType.float64, parallel_tim_sort[DType.float64]]("Parallel Tim sort", 3_000_000, builder, 200_000_000_000)
    benchmark[DType.float64, radix_sort[DType.float64]]("Radix sort", 3_000_000, builder, 200_000_000_000)
    benchmark[DType.float64, radix_sort13[DType.float64]]("Radix sort 13", 3_000_000, builder, 200_000_000_000)
    benchmark[DType.float64, radix_sort16]("Radix sort 16", 3_000_000, builder, 200_000_000_000)

    benchmark[DType.float64, std_sort[DType.float64]]("Std sort", 300, builder, 200)
    benchmark[DType.float64, quick_sort[DType.float64]]("Quick sort", 300, builder, 200)
    benchmark[DType.float64, tim_sort[DType.float64]]("Tim sort", 300, builder, 200)
    benchmark[DType.float64, parallel_tim_sort[DType.float64]]("Parallel Tim sort", 300, builder, 200)
    benchmark[DType.float64, radix_sort[DType.float64]]("Radix sort", 300, builder, 200)
    benchmark[DType.float64, radix_sort13[DType.float64]]("Radix sort 13", 300, builder, 200)
    benchmark[DType.float64, radix_sort16]("Radix sort 16", 300, builder, 200)

    # for i in range(1000, 200_000, 1000):
    #     benchmark[DType.float32, radix_sort[DType.float32]]("Radix sort", i, builder, 2_000_000_000)
    #     benchmark[DType.float64, radix_sort[DType.float64]]("Radix sort", i, builder, 2_000_000_000)
    #     benchmark[DType.float32, radix_sort11[DType.float32]]("Radix sort 11", i, builder, 2_000_000_000)
    #     benchmark[DType.float64, radix_sort13[DType.float64]]("Radix sort 13", i, builder, 2_000_000_000)

    print(builder^.finish())

--- benchmark_result_i7.csv ---

Algorithm name,DType,List size,Max value,Min total duration,Min duration per element,Sorted
Selection sort,uint8,5,3000,26,5,True
Insertion sort,uint8,5,3000,19,3,True
Std sort,uint8,5,3000,115,23,True
Quick sort,uint8,5,3000,35,7,True
Tim sort,uint8,5,3000,22,4,True
Counting sort,uint8,5,3000,518,103,True
Radix sort,uint8,5,3000,215,43,True
Selection sort,uint8,20,3000,131,6,True
Insertion sort,uint8,20,3000,54,2,True
Std sort,uint8,20,3000,154,7,True
Quick sort,uint8,20,3000,121,6,True
Tim sort,uint8,20,3000,107,5,True
Counting sort,uint8,20,3000,583,29,True
Radix sort,uint8,20,3000,228,11,True
Selection sort,uint8,50,3000,711,14,True
Insertion sort,uint8,50,3000,361,7,True
Std sort,uint8,50,3000,776,15,True
Quick sort,uint8,50,3000,516,10,True
Tim sort,uint8,50,3000,591,11,True
Counting sort,uint8,50,3000,677,13,True
Radix sort,uint8,50,3000,275,5,True
Selection sort,uint8,300,3000,32080,106,True
Insertion sort,uint8,300,3000,10218,34,True
Std sort,uint8,300,3000,5930,19,True
Quick sort,uint8,300,3000,4711,15,True
Tim sort,uint8,300,3000,6420,21,True
Counting sort,uint8,300,3000,1005,3,True
Radix sort,uint8,300,3000,581,1,True
Selection sort,int8,300,3000,27100,90,True
Insertion sort,int8,300,3000,8834,29,True
Std sort,int8,300,3000,6214,20,True
Quick sort,int8,300,3000,4265,14,True
Tim sort,int8,300,3000,4855,16,True
Parallel Tim sort,int8,300,3000,17922,59,True
Radix sort,int8,300,3000,490,1,True
Selection sort,uint16,3000,3000,2283103,761,True
Insertion sort,uint16,3000,3000,903909,301,True
Std sort,uint16,3000,3000,110652,36,True
Quick sort,uint16,3000,3000,124374,41,True
Tim sort,uint16,3000,3000,94588,31,True
Parallel Tim sort,uint16,3000,3000,64991,21,True
Counting sort,uint16,3000,3000,13572,4,True
Radix sort,uint16,3000,3000,7250,2,True
Selection sort,int16,3000,3000,2289158,763,True
Insertion sort,int16,3000,3000,888161,296,True
Std sort,int16,3000,3000,116783,38,True
Quick sort,int16,3000,3000,132958,44,True
Tim sort,int16,3000,3000,95076,31,True
Parallel Tim sort,int16,3000,3000,63027,21,True
Radix sort,int16,3000,3000,9221,3,True
Selection sort,float16,3000,3000,4196472,1398,True
Insertion sort,float16,3000,3000,2103017,701,True
Std sort,float16,3000,3000,224381,74,True
Quick sort,float16,3000,3000,151287,50,True
Tim sort,float16,3000,3000,162352,54,True
Parallel Tim sort,float16,3000,3000,87313,29,True
Radix sort,float16,3000,3000,10912,3,True
Std sort,uint32,300000,2000000000,21969772,73,True
Quick sort,uint32,300000,2000000000,21148206,70,True
Tim sort,uint32,300000,2000000000,15918864,53,True
Parallel Tim sort,uint32,300000,2000000000,6890569,22,True
Radix sort,uint32,300000,2000000000,2276633,7,True
Radix sort 11,uint32,300000,2000000000,2323702,7,True
Std sort,int32,300000,2000000000,21937529,73,True
Quick sort,int32,300000,2000000000,20777250,69,True
Tim sort,int32,300000,2000000000,16003725,53,True
Parallel Tim sort,int32,300000,2000000000,6844498,22,True
Radix sort,int32,300000,2000000000,2299347,7,True
Radix sort 11,int32,300000,2000000000,2352531,7,True
Std sort,float32,5000000,2000000000,504485446,100,True
Quick sort,float32,5000000,2000000000,448023078,89,True
Tim sort,float32,5000000,2000000000,349352149,69,True
Parallel Tim sort,float32,5000000,2000000000,135811541,27,True
Radix sort,float32,5000000,2000000000,50109996,10,True
Radix sort 11,float32,5000000,2000000000,47750934,9,True
Std sort,uint64,3000000,200000000000,283869222,94,True
Quick sort,uint64,3000000,200000000000,254671312,84,True
Tim sort,uint64,3000000,200000000000,195589013,65,True
Parallel Tim sort,uint64,3000000,200000000000,84258283,28,True
Radix sort,uint64,3000000,200000000000,77239379,25,True
Radix sort 13,uint64,3000000,200000000000,58921306,19,True
Std sort,int64,3000000,200000000000,264787746,88,True
Quick sort,int64,3000000,200000000000,244645935,81,True
Tim sort,int64,3000000,200000000000,193942385,64,True
Parallel Tim sort,int64,3000000,200000000000,83949517,27,True
Radix sort,int64,3000000,200000000000,77933791,25,True
Radix sort 13,int64,3000000,200000000000,58967876,19,True
Std sort,float64,3000000,200000000000,297268154,99,True
Quick sort,float64,3000000,200000000000,256048485,85,True
Tim sort,float64,3000000,200000000000,207751741,69,True
Parallel Tim sort,float64,3000000,200000000000,86875550,28,True
Radix sort,float64,3000000,200000000000,83552900,27,True
Radix sort 13,float64,3000000,200000000000,68885139,22,True
Radix sort 16,float64,3000000,200000000000,113632305,37,True
Std sort,float64,300,200,5216,17,True
Quick sort,float64,300,200,4392,14,True
Tim sort,float64,300,200,7840,26,True
Parallel Tim sort,float64,300,200,17869,59,True
Radix sort,float64,300,200,6472,21,True
Radix sort 13,float64,300,200,19146,63,True
Radix sort 16,float64,300,200,108474,361,True



--- benchmark_result_m1.csv ---

Algorithm name,DType,List size,Max value,Min total duration,Min duration per element,Sorted
Selection sort,uint8,5,3000,0,0,True
Insertion sort,uint8,5,3000,0,0,True
Std sort,uint8,5,3000,0,0,True
Quick sort,uint8,5,3000,0,0,True
Tim sort,uint8,5,3000,0,0,True
Counting sort,uint8,5,3000,0,0,True
Radix sort,uint8,5,3000,0,0,True
Selection sort,uint8,20,3000,0,0,True
Insertion sort,uint8,20,3000,0,0,True
Std sort,uint8,20,3000,0,0,True
Quick sort,uint8,20,3000,0,0,True
Tim sort,uint8,20,3000,0,0,True
Counting sort,uint8,20,3000,0,0,True
Radix sort,uint8,20,3000,0,0,True
Selection sort,uint8,50,3000,2000,40,True
Insertion sort,uint8,50,3000,0,0,True
Std sort,uint8,50,3000,1000,20,True
Quick sort,uint8,50,3000,0,0,True
Tim sort,uint8,50,3000,0,0,True
Counting sort,uint8,50,3000,0,0,True
Radix sort,uint8,50,3000,0,0,True
Selection sort,uint8,300,3000,82000,273,True
Insertion sort,uint8,300,3000,10000,33,True
Std sort,uint8,300,3000,5000,16,True
Quick sort,uint8,300,3000,3000,10,True
Tim sort,uint8,300,3000,6000,20,True
Counting sort,uint8,300,3000,1000,3,True
Radix sort,uint8,300,3000,0,0,True
Selection sort,int8,300,3000,83000,276,True
Insertion sort,int8,300,3000,10000,33,True
Std sort,int8,300,3000,5000,16,True
Quick sort,int8,300,3000,2000,6,True
Tim sort,int8,300,3000,5000,16,True
Parallel Tim sort,int8,300,3000,9000,30,True
Radix sort,int8,300,3000,0,0,True
Selection sort,uint16,3000,3000,8334000,2778,True
Insertion sort,uint16,3000,3000,826000,275,True
Std sort,uint16,3000,3000,125000,41,True
Quick sort,uint16,3000,3000,115000,38,True
Tim sort,uint16,3000,3000,92000,30,True
Parallel Tim sort,uint16,3000,3000,43000,14,True
Counting sort,uint16,3000,3000,10000,3,True
Radix sort,uint16,3000,3000,9000,3,True
Selection sort,int16,3000,3000,8314000,2771,True
Insertion sort,int16,3000,3000,820000,273,True
Std sort,int16,3000,3000,114000,38,True
Quick sort,int16,3000,3000,113000,37,True
Tim sort,int16,3000,3000,89000,29,True
Parallel Tim sort,int16,3000,3000,44000,14,True
Radix sort,int16,3000,3000,9000,3,True
Selection sort,float16,3000,3000,15164000,5054,True
Insertion sort,float16,3000,3000,821000,273,True
Std sort,float16,3000,3000,196000,65,True
Quick sort,float16,3000,3000,131000,43,True
Tim sort,float16,3000,3000,120000,40,True
Parallel Tim sort,float16,3000,3000,59000,19,True
Radix sort,float16,3000,3000,10000,3,True
Std sort,uint32,300000,2000000000,23014000,76,True
Quick sort,uint32,300000,2000000000,19129000,63,True
Tim sort,uint32,300000,2000000000,16276000,54,True
Parallel Tim sort,uint32,300000,2000000000,5938000,19,True
Radix sort,uint32,300000,2000000000,1638000,5,True
Radix sort 11,uint32,300000,2000000000,786000,2,True
Std sort,int32,300000,2000000000,22785000,75,True
Quick sort,int32,300000,2000000000,19529000,65,True
Tim sort,int32,300000,2000000000,16207000,54,True
Parallel Tim sort,int32,300000,2000000000,6052000,20,True
Radix sort,int32,300000,2000000000,1608000,5,True
Radix sort 11,int32,300000,2000000000,759000,2,True
Std sort,float32,5000000,2000000000,762011000,152,True
Quick sort,float32,5000000,2000000000,445898000,89,True
Tim sort,float32,5000000,2000000000,467927000,93,True
Parallel Tim sort,float32,5000000,2000000000,167810000,33,True
Radix sort,float32,5000000,2000000000,49216000,9,True
Radix sort 11,float32,5000000,2000000000,22456000,4,True
Std sort,uint64,3000000,200000000000,287128000,95,True
Quick sort,uint64,3000000,200000000000,228464000,76,True
Tim sort,uint64,3000000,200000000000,194782000,64,True
Parallel Tim sort,uint64,3000000,200000000000,71029000,23,True
Radix sort,uint64,3000000,200000000000,77167000,25,True
Radix sort 13,uint64,3000000,200000000000,34409000,11,True
Std sort,int64,3000000,200000000000,289589000,96,True
Quick sort,int64,3000000,200000000000,228832000,76,True
Tim sort,int64,3000000,200000000000,195940000,65,True
Parallel Tim sort,int64,3000000,200000000000,70936000,23,True
Radix sort,int64,3000000,200000000000,77585000,25,True
Radix sort 13,int64,3000000,200000000000,33498000,11,True
Std sort,float64,3000000,200000000000,426790000,142,True
Quick sort,float64,3000000,200000000000,260657000,86,True
Tim sort,float64,3000000,200000000000,275008000,91,True
Parallel Tim sort,float64,3000000,200000000000,99226000,33,True
Radix sort,float64,3000000,200000000000,61548000,20,True
Radix sort 13,float64,3000000,200000000000,27283000,9,True
Radix sort 16,float64,3000000,200000000000,29580000,9,True
Std sort,float64,300,200,8000,26,True
Quick sort,float64,300,200,3000,10,True
Tim sort,float64,300,200,7000,23,True
Parallel Tim sort,float64,300,200,9000,30,True
Radix sort,float64,300,200,5000,16,True
Radix sort 13,float64,300,200,13000,43,True
Radix sort 16,float64,300,200,67000,223,True



--- benchmark_results_string.csv ---

Description ,Min                   ,Max                   ,Mean                  ,Valid
report1_i1  ,  8670.4295000000002  ,  9500.9500000000007  ,  8829.007583147164   ,True
report1_i2  ,  9093.6499999999996  ,  9946.9372000000003  ,  9672.6625279123946  ,True
report1_i3  ,  9236.9116764821356  , 10092.879999999999   ,  9353.6179758703256  ,True
report1_q1  ,  2743.6100000000001  ,  2864.5824600000001  ,  2860.7301638016379  ,True
report1_q2  ,  2920.7449999999999  ,  3354.5999999999999  ,  2946.4195391953917  ,True
report1_q3  ,  2675.6869999999999  ,  3132.5999999999999  ,  2779.8596660966609  ,True
report1_mk  ,  6639.7950000000001  ,  7212.1499999999996  ,  6800.5854160434401  ,True
report2_i1  ,744033.54500000004    ,749001.58488228009    ,746875.64375340973    ,True
report2_i2  ,795199.76165113179    ,809460.47999999998    ,798331.27469316195    ,True
report2_i3  ,861132.96499999997    ,891410.40000000002    ,871370.87476751395    ,True
report2_q1  , 90793.070000000007   , 95843.25             , 92005.819681791399   ,True
report2_q2  , 76527.300000000003   , 78606.084000000003   , 77902.188653451813   ,True
report2_q3  , 85063.540839804424   , 90064.0              , 85297.308209047202   ,True
report2_mk  , 67112.149999999994   , 69623.619999999995   , 69162.064636451891   ,True
report3_i1  ,751833.5              ,760428.14500000002    ,759175.65609348915    ,True
report3_i2  ,806958.25             ,816323.73082099599    ,813639.94838709675    ,True
report3_i3  ,873776.39149560116    ,879470.10204081633    ,876736.68639798486    ,True
report3_q1  ,111312.82000000001    ,116434.85000000001    ,112345.99162401116    ,True
report3_q2  , 96744.550000000003   , 98665.184999999998   , 98275.350489685356   ,True
report3_q3  ,100821.22500000001    ,108237.25             ,101734.74620374991    ,True
report3_mk  , 62869.5              , 64815.38149105796    , 64699.896689442787   ,True
report4_i1  ,868929.40000000002    ,880799.15689149557    ,879898.7940619078     ,True
report4_i2  ,790169.80000000005    ,809604.32620320853    ,804411.38613861392    ,True
report4_i3  ,831506.62             ,850880.65000000002    ,832406.31809981959    ,True
report4_q1  ,103625.49000000001    ,106604.10293079096    ,106054.73119787438    ,True
report4_q2  , 74606.0              , 76254.075500000006   , 75364.190478826466   ,True
report4_q3  , 83998.54375876578    , 89821.050000000003   , 84094.012672811063   ,True
report4_mk  ,102392.89999999999    ,104794.155            ,104547.87280285901    ,True
report5_i1  ,735329.84999999998    ,740583.07160493825    ,739814.2721347094     ,True
report5_i2  ,803582.35790884716    ,806578.59999999998    ,804280.3366452367     ,True
report5_i3  ,882083.51479289937    ,902176.65000000002    ,883030.96317460318    ,True
report5_q1  , 84418.759999999995   , 87025.75             , 85613.668662797878   ,True
report5_q2  , 72070.600000000006   , 73738.111999999994   , 73298.788104490508   ,True
report5_q3  , 76725.389999999999   , 84705.25             , 77478.265300408762   ,True
report5_mk  , 75602.443044354834   , 83131.25             , 75791.774805151726   ,True
report6_i1  ,   154.61949999999999 ,   356.69999999999999 ,   163.84367028188402 ,True
report6_i2  ,   177.83150000000001 ,   591.25             ,   190.22664137576271 ,True
report6_i3  ,   135.44999999999999 ,   151.77500000000001 ,   148.29590415557033 ,True
report6_q1  ,   136.68186499999999 ,   175.58000000000001 ,   137.02628858616842 ,True
report6_q2  ,   136.17949999999999 ,   368.60000000000002 ,   146.42482569120287 ,True
report6_q3  ,   120.6023           ,   144.84999999999999 ,   125.54540954498935 ,True
report6_mk  ,   305.11045000000001 ,   522.29999999999995 ,   305.90837658540153 ,True
report7_i1  , 14617.4735           , 15562.695            , 15031.489655505629   ,True
report7_i2  , 13774.65             , 14742.3665           , 14718.916224569377   ,True
report7_i3  , 11105.532775141874   , 12392.965            , 11137.029871298782   ,True
report7_q1  , 20061.113099999999   , 21441.599999999999   , 20078.989966811823   ,True
report7_q2  , 18197.494999999999   , 18857.589490196078   , 18840.143673167575   ,True
report7_q3  , 16445.266725231286   , 16535.795999999998   , 16472.353697444483   ,True
report7_mk  , 20072.799999999999   , 21267.099999999999   , 20453.143672269394   ,True


--- benchmark_string_sort.mojo ---

from benchmark import run
from my_utils import corpus1, corpus2, corpus3, corpus4, corpus5, corpus6, corpus7
from insertion_sort import insertion_sort
from string_compare import lt
from multi_key_quicksort import multi_key_quicksort
from quick_sort import quick_sort
from radix_sorting import msb_radix_sort
from tim_sort import tim_sort, parallel_tim_sort
from csv import CsvBuilder

fn in_sort[get_corpus: fn () -> List[String], lte:fn (String, String) -> Bool]():
    var corpus = get_corpus()
    insertion_sort[String, lte](corpus)

fn mk_sort[get_corpus: fn () -> List[String]]():
    var corpus = get_corpus()
    multi_key_quicksort(corpus)

fn qk_sort[get_corpus: fn () -> List[String], lte:fn (String, String) -> Bool]():
    var corpus = get_corpus()
    quick_sort[String, lte](corpus)

fn msb_radix[get_corpus: fn () -> List[String]]():
    var corpus = get_corpus()
    msb_radix_sort(corpus)

fn std_sort[get_corpus: fn () -> List[String]]():
    var corpus = get_corpus()
    sort(corpus)

fn tm_sort[get_corpus: fn () -> List[String]]():
    var corpus = get_corpus()
    tim_sort(corpus)

fn ptm_sort[get_corpus: fn () -> List[String]]():
    var corpus = get_corpus()
    parallel_tim_sort(corpus)


fn report_mean[f: fn()-> None](description: StringLiteral):
    var report1_mk = run[f]()
    print(description, report1_mk.mean("ns"))

fn main():
    report_mean[in_sort[corpus1, lt]]("report1_ins")
    report_mean[qk_sort[corpus1, lt]]("report1_qks")
    report_mean[mk_sort[corpus1]]("report1_mks")
    report_mean[msb_radix[corpus1]]("report1_rdx")
    report_mean[std_sort[corpus1]]("report1_std")
    report_mean[tm_sort[corpus1]]("report1_tms")
    report_mean[ptm_sort[corpus1]]("report1_ptm")

    report_mean[in_sort[corpus2, lt]]("report2_ins")
    report_mean[qk_sort[corpus2, lt]]("report2_qks")
    report_mean[mk_sort[corpus2]]("report2_mks")
    report_mean[msb_radix[corpus2]]("report2_rdx")
    report_mean[std_sort[corpus2]]("report2_std")
    report_mean[tm_sort[corpus2]]("report2_tms")
    report_mean[ptm_sort[corpus2]]("report2_ptm")

    report_mean[in_sort[corpus3, lt]]("report3_ins")
    report_mean[qk_sort[corpus3, lt]]("report3_qks")
    report_mean[mk_sort[corpus3]]("report3_mks")
    report_mean[msb_radix[corpus3]]("report3_rdx")
    report_mean[std_sort[corpus3]]("report3_std")
    report_mean[tm_sort[corpus3]]("report3_tms")
    report_mean[ptm_sort[corpus3]]("report3_ptm")

    report_mean[in_sort[corpus4, lt]]("report4_ins")
    report_mean[qk_sort[corpus4, lt]]("report4_qks")
    report_mean[mk_sort[corpus4]]("report4_mks")
    report_mean[msb_radix[corpus4]]("report4_rdx")
    report_mean[std_sort[corpus4]]("report4_std")
    report_mean[tm_sort[corpus4]]("report4_tms")
    report_mean[ptm_sort[corpus4]]("report4_ptm")
    
    report_mean[in_sort[corpus5, lt]]("report5_ins")
    report_mean[qk_sort[corpus5, lt]]("report5_qks")
    report_mean[mk_sort[corpus5]]("report5_mks")
    report_mean[msb_radix[corpus5]]("report5_rdx")
    report_mean[std_sort[corpus5]]("report5_std")
    report_mean[tm_sort[corpus5]]("report5_tms")
    report_mean[ptm_sort[corpus5]]("report5_ptm")

    report_mean[in_sort[corpus6, lt]]("report6_ins")
    report_mean[qk_sort[corpus6, lt]]("report6_qks")
    report_mean[mk_sort[corpus6]]("report6_mks")
    report_mean[msb_radix[corpus6]]("report6_rdx")
    report_mean[std_sort[corpus6]]("report6_std")
    report_mean[tm_sort[corpus6]]("report6_tms")
    report_mean[ptm_sort[corpus6]]("report6_ptm")

    report_mean[in_sort[corpus7, lt]]("report7_ins")
    report_mean[qk_sort[corpus7, lt]]("report7_qks")
    report_mean[mk_sort[corpus7]]("report7_mks")
    report_mean[msb_radix[corpus7]]("report7_rxd")
    report_mean[std_sort[corpus7]]("report7_std")
    report_mean[tm_sort[corpus7]]("report7_tms")
    report_mean[ptm_sort[corpus7]]("report7_ptm")
    



--- binary_search/__init__.mojo ---

from sys.intrinsics import PrefetchOptions

fn classic_binary_search[D: DType](b: DTypePointer[D], n: Int, x: SIMD[D, 1]) -> Int:
    var low = 0
    var high = n - 1
    while low <= high:
        var mid = (high + low) >> 1
        var mid_value = b.load(mid)
        if mid_value < x:
            low = mid + 1
        elif mid_value > x:
            high = mid - 1
        else:
            return mid
    return -1
        

fn classic_binary_search[D: AnyType, lt: fn(D, D) -> Bool](b: Pointer[D], n: Int, x: D) -> Int:
    var low = 0
    var high = n - 1
    while low <= high:
        var mid = (high + low) >> 1
        var mid_value = b.load(mid)
        if lt(mid_value, x):
            low = mid + 1
        elif lt(mid_value, x):
            high = mid - 1
        else:
            return mid
    return -1

# Binary search based on https://en.algorithmica.org/hpc/data-structures/binary-search/

fn binary_search[D: AnyType, lt: fn(D, D) -> Int, eq: fn(D, D) -> Bool](b: Pointer[D], n: Int, x: D) -> Int:
    var cursor = 0
    var length = n
    while length > 1:
        var half = length >> 1
        length -= half
        cursor += lt(b.load(cursor + half - 1), x) * half

    return cursor if eq(b.load(cursor), x) else -1

fn binary_search[D: DType, with_prefetch: Bool = False](b: DTypePointer[D], n: Int, x: SIMD[D, 1]) -> Int:
    var cursor = 0
    var length = n
    alias pfo = PrefetchOptions().for_read()
    while length > 1:
        var half = length >> 1
        length -= half
        var next_half = (length >> 1) - 1
        @parameter
        if with_prefetch:
            b.offset(cursor + next_half).prefetch[pfo]()
            b.offset(cursor + half + next_half).prefetch[pfo]()
        cursor += int(b.load(cursor + half - 1) < x) * half

    return cursor if b.load(cursor) == x else -1


--- binary_search_benchmark.mojo ---

from binary_search import binary_search, classic_binary_search
from benchmark import run
from random import random_ui64

fn main():
    var l = List[UInt64]()
    var count = 200
    var searches = 5_000_000
    for i in range(count):
        l.append(i)
    
    var total = 0
    var random_elements = List[UInt64](capacity=searches)
    for _ in range(searches):
        var value = random_ui64(0, count - 1)
        total += int(value)
        random_elements.append(value)

    var total1 = 0
    @parameter
    fn binary_search_random_items():
        total1 = 0
        for i in range(searches):
            total1 += binary_search[with_prefetch=False](l.data, len(l), random_elements[i])
    
    var total2 = 0
    @parameter
    fn classic_binary_search_random_items():
        total2 = 0
        for i in range(searches):
            total2 += classic_binary_search(l.data, len(l), random_elements[i])

    var result = run[binary_search_random_items]()
    result.print_full()

    var result_classic = run[classic_binary_search_random_items]()
    result_classic.print_full()

    print("Classic", result_classic.mean() / result.mean())
    print(total, total1, total2)

--- checkout_remote_modules.sh ---

#!/bin/bash

function check_out_remote_module() (
    rurl="$1"
    shift
    declare -a paths
    declare -a module_names
    for var in "$@" 
    do
        IFS="="
        read -ra module_name_components <<< "$var"
        components_count=${#module_name_components[@]}
        path=${module_name_components[0]}
        module_name=${module_name_components[$components_count-1]}
        paths=("${paths[@]}" "$path")
        module_names=("${module_names[@]}" "$module_name")
    done
    IFS=" "

    for module_name in "${module_names[@]}" 
    do
        rm -rf ../$module_name
    done
    
    current_date_time=$(date)
    echo "URL: $rurl"
    git clone -n --depth=1 --filter=tree:0 $rurl
    cd ${rurl##*/}
    git sparse-checkout set --no-cone "${paths[@]}"
    git checkout
    
    for i in "${!paths[@]}" 
    do
        module_name=${module_names[$i]}
        path=${paths[$i]}
        cp -R ./$path ../../$module_name
        echo $current_date_time > ../../$module_name/.checkoutinfo
        echo "URL: $rurl" >> ../../$module_name/.checkoutinfo
        echo "Path: $path" >> ../../$module_name/.checkoutinfo
    done
    cd ../
)

function checkout()(
    # Add check out remote module calls here
    
    # check_out_remote_module "https://github.com/mzaks/mojo-hash" "my_utils=hash_utils"
    check_out_remote_module "https://github.com/mzaks/mojo-csv" "csv"
)

mkdir -p "_deps"
cd "_deps"

checkout

rm -rf "../_deps"

--- count_sort/__init__.mojo ---


from memory import memset_zero, memcpy

fn counting_sort[D: DType](inout vector: List[SIMD[D, 1]]):
    var size = len(vector)
    var output = List[SIMD[D, 1]](capacity=size)
    var max_value = 0
    for i in range(size):
        if vector[i] > max_value:
            max_value = int(vector[i])
        output.append(0)

    var counts = List[Int](capacity=max_value + 1)
    counts.resize(max_value + 1, 0)

    for i in range(size):
        counts[int(vector[i])] += 1
    
    for i in range(1, len(counts)):
        counts[i] += counts[i - 1]

    var i = size - 1
    while i >= 0:
        output[counts[int(vector[i])] - 1] = vector[i]
        counts[int(vector[i])] -= 1
        i -= 1
    
    vector = output

--- counting_sort_sample.mojo ---

from my_utils import print_v
from count_sort import counting_sort

fn main():
    var v1 = List[UInt32]()
    v1.append(13)
    v1.append(31)
    v1.append(1)
    v1.append(7)
    v1.append(7)
    v1.append(4513)
    v1.append(45)

    print_v(v1)

    counting_sort(v1)
    print_v(v1)

--- csv/.checkoutinfo ---

Sun May 26 14:06:31 CEST 2024
URL: https://github.com/mzaks/mojo-csv
Path: csv


--- csv/__init__.mojo ---

from .csv_builder import CsvBuilder
from .csv_table import CsvTable


--- csv/csv_builder.mojo ---

from memory.memory import memcpy
from buffer import Buffer, Dim
from .string_utils import find_indices, contains_any_of, string_from_pointer

alias BufferType = Buffer[DType.int8]
alias CR_CHAR = "\r"
alias CR = ord(CR_CHAR)
alias LF_CHAR = "\n"
alias LF = ord(LF_CHAR)
alias COMMA_CHAR = ","
alias COMMA = ord(COMMA_CHAR)
alias QUOTE_CHAR = '"'
alias QUOTE = UInt8(ord(QUOTE_CHAR))


struct CsvBuilder:
    var _buffer: DTypePointer[DType.uint8]
    var _capacity: Int
    var num_bytes: Int
    var _column_count: Int
    var _elements_count: Int
    var _finished: Bool

    fn __init__(inout self, column_count: Int):
        self._capacity = 1024
        self._buffer = DTypePointer[DType.uint8].alloc(self._capacity)
        self._column_count = column_count
        self._elements_count = 0
        self._finished = False
        self.num_bytes = 0

    fn __init__(inout self, *coulmn_names: StringLiteral):
        self._capacity = 1024
        self._buffer = DTypePointer[DType.uint8].alloc(self._capacity)
        self._elements_count = 0
        self._finished = False
        self.num_bytes = 0

        var column_name_list: VariadicList[StringLiteral] = coulmn_names
        self._column_count = len(column_name_list)
        for i in range(len(column_name_list)):
            self.push(coulmn_names[i])

    fn __del__(owned self):
        if not self._finished:
            self._buffer.free()

    fn push[D: DType](inout self, value: SIMD[D, 1]):
        var s = String(value)
        var size = len(s)
        self.push(s, False)

    fn push_stringabel[T: Stringable](inout self, value: T, consider_escaping: Bool = False):
        self.push(str(value), consider_escaping)

    fn push_empty(inout self):
        self.push("", False)

    fn fill_up_row(inout self):
        var num_empty = self._column_count - (self._elements_count % self._column_count)
        if num_empty < self._column_count:
            for _ in range(num_empty):
                self.push_empty()

    fn push(inout self, s: String, consider_escaping: Bool = True):
        if consider_escaping and contains_any_of(
            s, CR_CHAR, LF_CHAR, COMMA_CHAR, QUOTE_CHAR
        ):
            return self.push(QUOTE_CHAR + escape_quotes_in(s) + QUOTE_CHAR, False)

        var size = len(s)
        self._extend_buffer_if_needed(size + 2)
        if self._elements_count > 0:
            if self._elements_count % self._column_count == 0:
                self._buffer.offset(self.num_bytes).store(CR)
                self._buffer.offset(self.num_bytes + 1).store(LF)
                self.num_bytes += 2
            else:
                self._buffer.offset(self.num_bytes).store(COMMA)
                self.num_bytes += 1

        memcpy(self._buffer.offset(self.num_bytes), s.unsafe_uint8_ptr(), size)
        s._strref_keepalive()

        self.num_bytes += size
        self._elements_count += 1

    @always_inline
    fn _extend_buffer_if_needed(inout self, size: Int):
        if self.num_bytes + size < self._capacity:
            return
        var new_size = self._capacity
        while new_size < self.num_bytes + size:
            new_size *= 2
        var p = DTypePointer[DType.uint8].alloc(new_size)
        memcpy(p, self._buffer, self.num_bytes)
        self._buffer.free()
        self._capacity = new_size
        self._buffer = p

    fn finish(owned self) -> String:
        self._finished = True
        self.fill_up_row()
        self._buffer.offset(self.num_bytes).store(CR)
        self._buffer.offset(self.num_bytes + 1).store(LF)
        self.num_bytes += 3
        return string_from_pointer(self._buffer, self.num_bytes)


fn escape_quotes_in(s: String) -> String:
    var indices = find_indices(s, QUOTE_CHAR)
    var i_size = len(indices)
    if i_size == 0:
        return s

    var size = len(s._buffer)
    var p_current = s.unsafe_uint8_ptr()
    var p_result = DTypePointer[DType.uint8].alloc(size + i_size)
    var first_index = int(indices[0])
    memcpy(p_result, p_current, first_index)
    p_result.offset(first_index).store(QUOTE)
    var offset = first_index + 1
    for i in range(1, len(indices)):
        var c_offset = int(indices[i - 1])
        var length = int(indices[i]) - c_offset
        memcpy(p_result.offset(offset), p_current.offset(c_offset), length)
        offset += length
        p_result.offset(offset).store(QUOTE)
        offset += 1

    var last_index = int(indices[i_size - 1])
    memcpy(p_result.offset(offset), p_current.offset(last_index), size - last_index)
    return string_from_pointer(p_result, size + i_size)


--- csv/csv_table.mojo ---

from .string_utils import find_indices, string_from_pointer
from algorithm.functional import vectorize
from sys.info import simdwidthof
from sys.intrinsics import compressed_store
from math import iota, reduce_bit_count
from memory import stack_allocation


alias QUOTE = ord('"')
alias COMMA = ord(",")
alias LF = ord("\n")
alias CR = ord("\r")
alias simd_width_u8 = simdwidthof[DType.uint8]()

struct CsvTable[sep: Int = COMMA]:
    var _inner_string: String
    var _starts: List[Int]
    var _ends: List[Int]
    var column_count: Int

    fn __init__(inout self, owned s: String, with_simd: Bool = True):
        self._inner_string = s
        self._starts = List[Int](capacity=10)
        self._ends = List[Int](capacity=10)
        self.column_count = -1
        if with_simd:
            self._simd_parse()
        else:
            self._parse()

    @always_inline
    fn _parse(inout self):
        var length = len(self._inner_string)
        var offset = 0
        var in_double_quotes = False
        self._starts.append(offset)
        while offset < length:
            var c = self._inner_string._buffer[offset]
            if c == QUOTE:
                in_double_quotes = not in_double_quotes
                offset += 1
            elif not in_double_quotes and c == sep:
                self._ends.append(offset)
                offset += 1
                self._starts.append(offset)
            elif not in_double_quotes and c == LF:
                self._ends.append(offset)
                if self.column_count == -1:
                    self.column_count = len(self._ends)
                offset += 1
                self._starts.append(offset)
            elif (
                not in_double_quotes
                and c == CR
                and length > offset + 1
                and self._inner_string._buffer[offset + 1] == LF
            ):
                self._ends.append(offset)
                if self.column_count == -1:
                    self.column_count = len(self._ends)
                offset += 2
                self._starts.append(offset)
            else:
                offset += 1

        if self._inner_string[length - 1] == "\n":
            _ = self._starts.pop()
        else:
            self._ends.append(length)

    @always_inline
    fn _simd_parse(inout self):
        var p = DTypePointer(self._inner_string.unsafe_uint8_ptr())
        var string_byte_length = len(self._inner_string)
        var in_quotes = False
        var last_chunk__ends_on_cr = False
        self._starts.append(0)

        @always_inline
        @parameter
        fn find_indicies[simd_width: Int](offset: Int):
            var chars = p.load[width=simd_width](offset)
            var quotes = chars == QUOTE
            var separators = chars == sep
            var lfs = chars == LF
            var all_bits = quotes | separators | lfs
            var crs = chars == CR

            var offsets = iota[DType.uint8, simd_width]()
            var sp: DTypePointer[DType.uint8] = stack_allocation[
                simd_width, UInt8, simd_width
            ]()
            compressed_store(offsets, sp, all_bits)
            var all_len = reduce_bit_count(all_bits)

            for i in range(all_len):
                var index = int(sp.load(i))
                if quotes[index]:
                    in_quotes = not in_quotes
                    continue
                if in_quotes:
                    continue
                var current_offset = index + offset
                var rs_compensation: Int
                if index > 0:
                    rs_compensation = int(lfs[index] & crs[index - 1])
                else:
                    rs_compensation = int(lfs[index] & last_chunk__ends_on_cr)
                self._ends.append(current_offset - rs_compensation)
                self._starts.append(current_offset + 1)
                if self.column_count == -1 and lfs[index]:
                    self.column_count = len(self._ends)
            last_chunk__ends_on_cr = crs[simd_width - 1]

        vectorize[find_indicies, simd_width_u8](string_byte_length)
        if self._inner_string[string_byte_length - 1] == "\n":
            _ = self._starts.pop()
        else:
            self._ends.append(string_byte_length)

    fn get(self, row: Int, column: Int) -> String:
        if column >= self.column_count:
            return ""

        var index = self.column_count * row + column
        if index >= len(self._ends):
            return ""

        if (
            self._inner_string[self._starts[index]] == '"'
            and self._inner_string[self._ends[index] - 1] == '"'
        ):
            var start = self._starts[index] + 1
            var length = (self._ends[index] - 1) - start
            var p1 = Pointer[UInt8].alloc(length + 1)
            memcpy(p1, self._inner_string.unsafe_uint8_ptr().offset(start), length)
            var _inner_string = string_from_pointer(p1, length + 1)
            var quote_indices = find_indices(_inner_string, '"')
            var quotes_count = len(quote_indices)
            if quotes_count == 0 or quotes_count & 1 == 1:
                return _inner_string

            var p = _inner_string.unsafe_uint8_ptr()
            var length2 = length - (quotes_count >> 1)
            var p2 = Pointer[UInt8].alloc(length2 + 1)
            var offset2 = 0
            memcpy(p2, p, int(quote_indices[0]))
            offset2 += int(quote_indices[0])

            for i in range(2, quotes_count, 2):
                var start = int(quote_indices[i - 1])
                var size = int(quote_indices[i]) - start
                memcpy(p2.offset(offset2), p.offset(start), size)
                offset2 += size
            var last = int(quote_indices[quotes_count - 1])
            memcpy(p2.offset(offset2), p.offset(last), length - last)
            _inner_string._strref_keepalive()
            return string_from_pointer(p2, length - (quotes_count >> 1) + 1)

        return self._inner_string[self._starts[index] : self._ends[index]]

    fn row_count(self) -> Int:
        return len(self._starts) // self.column_count


--- csv/string_utils.mojo ---

from algorithm.functional import vectorize
from sys.info import simdwidthof
from sys.intrinsics import compressed_store
from math import iota, reduce_bit_count, any_true
from memory import stack_allocation
from time import now
from collections.vector import InlinedFixedVector

alias simd_width_i8 = simdwidthof[DType.int8]()

fn vectorize_and_exit[simd_width: Int, workgroup_function: fn[i: Int](Int) capturing -> Bool](size: Int):
    var loops = size // simd_width
    for i in range(loops):
        if workgroup_function[simd_width](i * simd_width):
            return
    
    var rest = size & (simd_width - 1)
    @parameter
    if simd_width >= 64:
        if rest >= 32:
            if workgroup_function[32](size - rest):
                return
            rest -= 32
    @parameter
    if simd_width >= 32:
        if rest >= 16:
            if workgroup_function[16](size - rest):
                return
            rest -= 16
    @parameter
    if simd_width >= 16:
        if rest >= 8:
            if workgroup_function[8](size - rest):
                return
            rest -= 8
    @parameter
    if simd_width >= 8:
        if rest >= 4:
            if workgroup_function[4](size - rest):
                return
            rest -= 4
    @parameter
    if simd_width >= 4:
        if rest >= 2:
            if workgroup_function[2](size - rest):
                return
            rest -= 2

    if rest == 1:
        _= workgroup_function[1](size - rest)


fn find_indices(s: String, c: String) -> List[UInt64]:
    var size = len(s)
    var result = List[UInt64]()
    var char = UInt8(ord(c))
    var p = DTypePointer(s.unsafe_uint8_ptr())

    @parameter
    fn find[simd_width: Int](offset: Int):
        @parameter
        if simd_width == 1:
            if p.offset(offset).load() == char:
                return result.append(offset)
        else:
            var chunk = p.load[width=simd_width](offset)
            var occurrence = chunk == char
            var offsets = iota[DType.uint64, simd_width]() + offset
            var occurrence_count = reduce_bit_count(occurrence)
            var current_len = len(result)
            result.reserve(current_len + occurrence_count)
            result.resize(current_len + occurrence_count, 0)
            compressed_store(offsets, DTypePointer[DType.uint64](result.data).offset(current_len), occurrence)

    vectorize[find, simd_width_i8](size)
    return result


fn occurrence_count(s: String, *c: String) -> Int:
    var size = len(s)
    var result = 0
    var chars = InlinedFixedVector[UInt8](len(c))
    for i in range(len(c)):
        chars.append(UInt8(ord(c[i])))
    var p = DTypePointer(s.unsafe_uint8_ptr())

    @parameter
    fn find[simd_width: Int](offset: Int):
        @parameter
        if simd_width == 1:
            for i in range(len(chars)):
                var char = chars[i]
                if p.offset(offset).load() == char:
                    result += 1
                    return
        else:
            var chunk = p.load[width=simd_width](offset)

            var occurrence = SIMD[DType.bool, simd_width](False)
            for i in range(len(chars)):
                occurrence |= chunk == chars[i]
            var occurrence_count = reduce_bit_count(occurrence)
            result += occurrence_count

    vectorize[find, simd_width_i8](size)
    return result


fn contains_any_of(s: String, *c: String) -> Bool:
    var size = len(s)
    # var c_list: VariadicListMem[String] = c
    var chars = InlinedFixedVector[UInt8](len(c))
    for i in range(len(c)):
        chars.append(UInt8(ord(c[i])))
    var p = DTypePointer(s.unsafe_uint8_ptr())
    var flag = False

    @parameter
    fn find[simd_width: Int](i: Int) -> Bool:
        var chunk = p.load[width=simd_width]()
        p = p.offset(simd_width)
        for i in range(len(chars)):
            var occurrence = chunk == chars[i]
            if any_true(occurrence):
                flag = True
                return flag
        return False

    vectorize_and_exit[simd_width_i8, find](size)

    return flag


@always_inline
fn string_from_pointer(p: DTypePointer[DType.uint8], length: Int) -> String:
    # Since Mojo 0.5.0 the pointer needs to provide a 0 terminated byte string
    p.store(length - 1, 0)
    return String(p, length)


fn print_v(v: List[UInt64]):
    print("(" +  str(len(v)) + ")[")
    for i in range(len(v)):
        var end = ", " if i < len(v) - 1 else "]\n"
        print(v[i], end=end)


--- hash_utils/.checkoutinfo ---

Sun Nov 12 09:36:05 CET 2023
URL: https://github.com/mzaks/mojo-hash
Path: my_utils


--- hash_utils/__init__.mojo ---

from math import min

fn int_cmp(a: UInt32, b: UInt32) -> Int:
    return int(a) - int(b)

fn int_cmp64(a: UInt64, b: UInt64) -> Int:
    return int(a) - int(b)

fn int_to_str(a: UInt32) -> String:
    return String(a)

fn int_to_str64(a: UInt64) -> String:
    return String(a)

fn cmp_strl(a: StringLiteral, b: StringLiteral) -> Int:
    var l = min(len(a), len(b))
    var p1 = a.data().bitcast[DType.uint8]()
    var p2 = b.data().bitcast[DType.uint8]()
    var diff = memcmp(p1, p2, l)

    return diff if diff != 0 else len(a) - len(b)

fn stsl(a: StringLiteral) -> String:
    return a



--- insertion_sort/__init__.mojo ---

from .sort import insertion_sort

--- insertion_sort/sort.mojo ---

fn insertion_sort[D: DType](inout vector: List[SIMD[D, 1]]):
    for i in range(1, len(vector)):
        var key = vector[i]
        var j = i - 1
        while j >= 0 and key < vector[j]:
            vector[j + 1] = vector[j]
            j -= 1
        vector[j + 1] = key

fn insertion_sort[D: CollectionElement, lt: fn(D, D) -> Bool](inout vector: List[D]):
    for i in range(1, len(vector)):
        var key = vector[i]
        var j = i - 1
        while j >= 0 and lt(key, vector[j]):
            vector[j + 1] = vector[j]
            j -= 1
        vector[j + 1] = key


--- insertion_sort_sample.mojo ---

from my_utils import *
from string_compare import lt
from insertion_sort import insertion_sort
from time import now


fn main():
    var v1 = List[UInt32]()
    v1.append(13)
    v1.append(31)
    v1.append(1)
    v1.append(7)
    v1.append(7)
    v1.append(4513)
    v1.append(45)

    print_v(v1)

    insertion_sort[DType.uint32](v1)
    
    print_v(v1)

    var v2 = List(str("d"), str("a"), str("bb"), str("ab"), str("dfg"), str("efgds"))
    print_v(v2)
    
    insertion_sort[String, lt](v2)
    print_v(v2)

    var corpus = corpus7()
    var tik = now()
    insertion_sort[String, lt](corpus)
    var tok = now()
    print_v(corpus)
    print(tok - tik)

    corpus = corpus3()
    tik = now()
    insertion_sort[String, lt](corpus)
    tok = now()
    print_v(corpus)
    print(tok - tik)


--- multi_key_quicksort/__init__.mojo ---

from .sort import multi_key_quicksort

--- multi_key_quicksort/sort.mojo ---

# based on https://github.com/rantala/string-sorting/blob/master/external/multikey.c

from random import random_ui64

@always_inline
fn _vec_swap(inout values: List[String], a: Int, b: Int, n: Int):
    var n1 = n
    var a1 = a
    var b1 = b
    while n1 > 0:
        _exchange(values, a1, b1)
        a1 +=1
        b1 +=1
        n1 -= 1

@always_inline
fn _exchange(inout values: List[String], a: Int, b: Int):
    values[a], values[b] = values[b], values[a]

@always_inline
fn _char_at(s: String, index: Int) -> Int:
    if len(s) <= index:
        return 0
    return int(DTypePointer(s.unsafe_uint8_ptr()).load(index))

fn _mk_sort(inout values: List[String], n: Int, depth: Int, offset: Int):
    if n <= 1:
        return
    var a = offset + int(random_ui64(0, n - 1))
    _exchange(values, offset, a)
    var v = _char_at(values[offset], depth)
    a = offset + 1
    var b = offset + 1
    var c = offset + n - 1
    var d = offset + n - 1
    var r: Int
    while True:
        while b <= c and _char_at(values[b], depth) - v <= 0:
            if _char_at(values[b], depth) - v == 0:
                _exchange(values, a, b)
                a += 1
            b += 1
        while b <= c and _char_at(values[c], depth) - v >= 0:
            if _char_at(values[c], depth) - v == 0:
                _exchange(values, c, d)
                d -= 1
            c -= 1
        if b > c:
            break
        _exchange(values, b, c)
        b += 1
        c -= 1
    r = min(a, b - a)
    _vec_swap(values, offset, b-r, r)
    r = min(d-c, n-d-1)
    _vec_swap(values, b, n-r, r)
    r = b-a
    _mk_sort(values, r, depth, offset)
    if _char_at(values[r], depth) != 0:
        _mk_sort(values, a + n-d-1, depth + 1, offset + r)
    r = d-c
    _mk_sort(values, r, depth, offset + n - r)

fn multi_key_quicksort(inout values: List[String]):
    _mk_sort(values, len(values), 0, 0)

--- multi_key_sort_sample.mojo ---

from multi_key_quicksort import multi_key_quicksort
from my_utils import print_v, corpus4, corpus7
from time import now


fn main():
    var v1 = List[String]("a", "d")
    print_v(v1)
    multi_key_quicksort(v1)
    print_v(v1)

    v1 = List[String]("sam", "same", "her", "make", "zebra")
    print_v(v1)
    multi_key_quicksort(v1)
    print_v(v1)

    v1 = List[String]("d", "a", "bb", "ab", "dfg", "efgds")
    var start = now()
    print_v(v1)
    multi_key_quicksort(v1)
    print_v(v1)
    print(now() - start)

    var corpus = corpus7()
    print_v(corpus)
    var tik = now()
    multi_key_quicksort(corpus)
    var tok = now()
    print("----")
    print_v(corpus)
    print(tok - tik)

    print("===")
    corpus = corpus4()
    print_v(corpus)
    tik = now()
    multi_key_quicksort(corpus)
    tok = now()
    print("----")
    print_v(corpus)
    print(tok - tik)

--- my_utils.mojo ---

trait StringableCollectionElement(Stringable, CollectionElement):
    pass

fn print_v[T: StringableCollectionElement](values: List[T], end: StringLiteral = "|"):
    for v in values:
        print(v[], end=end)
    print()

# fn lte_s(a: String, b: String) -> Bool:
#     var l1 = len(a)
#     var l2 = len(b)
#     var min_len = l1 if l1 <= l2 else l2
#     var res = memcmp(a._as_ptr(), b._as_ptr(), min_len)
#     return len(a) <= len(b) if res == 0 else res < 0

# @always_inline
# fn lt(a: String, b: String) -> Bool:
#     var len1 = len(a)
#     var len2 = len(b)
#     if len1 < len2:
#         return memcmp(a._as_ptr(), b._as_ptr(), len1) <= 0
#     else:
#         return memcmp(a._as_ptr(), b._as_ptr(), len2) < 0

fn corpus1() -> List[String]:
    return List[String]('Lorem', 'ipsum', 'dolor', 'sit', 'amet,', 'consectetur', 'adipiscing', 'elit.', 'Quisque', 'orci', 'urna,', 'pretium', 'et', 'porta', 'ac,', 'porttitor', 'sit', 'amet', 'sem.', 'Fusce', 'sagittis', 'lorem', 'neque,', 'vitae', 'sollicitudin', 'elit', 'suscipit', 'et.', 'In', 'interdum', 'convallis', 'nisl', 'in', 'ornare.', 'Vestibulum', 'ante', 'ipsum', 'primis', 'in', 'faucibus', 'orci', 'luctus', 'et', 'ultrices', 'posuere', 'cubilia', 'curae;', 'Aliquam', 'erat', 'volutpat.', 'Morbi', 'mollis', 'iaculis', 'lectus', 'ac', 'tincidunt.', 'Fusce', 'nisi', 'lacus,', 'semper', 'eu', 'dignissim', 'et,', 'malesuada', 'non', 'mi.', 'Sed', 'euismod', 'urna', 'vel', 'elit', 'faucibus,', 'eu', 'bibendum', 'ante', 'fringilla.', 'Curabitur', 'tempus', 'in', 'turpis', 'at', 'mattis.', 'Aliquam', 'erat', 'volutpat.', 'Donec', 'maximus', 'elementum', 'felis,', 'sit', 'amet', 'dignissim', 'augue', 'tincidunt', 'blandit.', 'Aliquam', 'fermentum,', 'est', 'eu', 'mollis.')

fn corpus2() -> List[String]:
    return List[String]('But', 'I', 'must', 'explain', 'to', 'you', 'how', 'all', 'this', 'mistaken', 'idea', 'of', 'denouncing', 'pleasure', 'and', 'praising', 'pain', 'was', 'born', 'and', 'I', 'will', 'give', 'you', 'a', 'complete', 'account', 'of', 'the', 'system,', 'and', 'expound', 'the', 'actual', 'teachings', 'of', 'the', 'great', 'explorer', 'of', 'the', 'truth,', 'the', 'master-builder', 'of', 'human', 'happiness.', 'No', 'one', 'rejects,', 'dislikes,', 'or', 'avoids', 'pleasure', 'itself,', 'because', 'it', 'is', 'pleasure,', 'but', 'because', 'those', 'who', 'do', 'not', 'know', 'how', 'to', 'pursue', 'pleasure', 'rationally', 'encounter', 'consequences', 'that', 'are', 'extremely', 'painful.', 'Nor', 'again', 'is', 'there', 'anyone', 'who', 'loves', 'or', 'pursues', 'or', 'desires', 'to', 'obtain', 'pain', 'of', 'itself,', 'because', 'it', 'is', 'pain,', 'but', 'because', 'occasionally', 'circumstances', 'occur', 'in', 'which', 'toil', 'and', 'pain', 'can', 'procure', 'him', 'some', 'great', 'pleasure.', 'To', 'take', 'a', 'trivial', 'example,', 'which', 'of', 'us', 'ever', 'undertakes', 'laborious', 'physical', 'exercise,', 'except', 'to', 'obtain', 'some', 'advantage', 'from', 'it?', 'But', 'who', 'has', 'any', 'right', 'to', 'find', 'fault', 'with', 'a', 'man', 'who', 'chooses', 'to', 'enjoy', 'a', 'pleasure', 'that', 'has', 'no', 'annoying', 'consequences,', 'or', 'one', 'who', 'avoids', 'a', 'pain', 'that', 'produces', 'no', 'resultant', 'pleasure?', 'On', 'the', 'other', 'hand,', 'we', 'denounce', 'with', 'righteous', 'indignation', 'and', 'dislike', 'men', 'who', 'are', 'so', 'beguiled', 'and', 'demoralized', 'by', 'the', 'charms', 'of', 'pleasure', 'of', 'the', 'moment,', 'so', 'blinded', 'by', 'desire,', 'that', 'they', 'cannot', 'foresee', 'the', 'pain', 'and', 'trouble', 'that', 'are', 'bound', 'to', 'ensue;', 'and', 'equal', 'blame', 'belongs', 'to', 'those', 'who', 'fail', 'in', 'their', 'duty', 'through', 'weakness', 'of', 'will,', 'which', 'is', 'the', 'same', 'as', 'saying', 'through', 'shrinking', 'from', 'toil', 'and', 'pain.', 'These', 'cases', 'are', 'perfectly', 'simple', 'and', 'easy', 'to', 'distinguish.', 'In', 'a', 'free', 'hour,', 'when', 'our', 'power', 'of', 'choice', 'is', 'untrammelled', 'and', 'when', 'nothing', 'prevents', 'our', 'being', 'able', 'to', 'do', 'what', 'we', 'like', 'best,', 'every', 'pleasure', 'is', 'to', 'be', 'welcomed', 'and', 'every', 'pain', 'avoided.', 'But', 'in', 'certain', 'circumstances', 'and', 'owing', 'to', 'the', 'claims', 'of', 'duty', 'or', 'the', 'obligations', 'of', 'business', 'it', 'will', 'frequently', 'occur', 'that', 'pleasures', 'have', 'to', 'be', 'repudiated', 'and', 'annoyances', 'accepted.', 'The', 'wise', 'man', 'therefore', 'always', 'holds', 'in', 'these', 'matters', 'to', 'this', 'principle', 'of', 'selection:', 'he', 'rejects', 'pleasures', 'to', 'secure', 'other', 'greater', 'pleasures,', 'or', 'else', 'he', 'endures', 'pains', 'to', 'avoid', 'worse', 'pains.', 'But', 'I', 'must', 'explain', 'to', 'you', 'how', 'all', 'this', 'mistaken', 'idea', 'of', 'denouncing', 'pleasure', 'and', 'praising', 'pain', 'was', 'born', 'and', 'I', 'will', 'give', 'you', 'a', 'complete', 'account', 'of', 'the', 'system,', 'and', 'expound', 'the', 'actual', 'teachings', 'of', 'the', 'great', 'explorer', 'of', 'the', 'truth,', 'the', 'master-builder', 'of', 'human', 'happiness.', 'No', 'one', 'rejects,', 'dislikes,', 'or', 'avoids', 'pleasure', 'itself,', 'because', 'it', 'is', 'pleasure,', 'but', 'because', 'those', 'who', 'do', 'not', 'know', 'how', 'to', 'pursue', 'pleasure', 'rationally', 'encounter', 'consequences', 'that', 'are', 'extremely', 'painful.', 'Nor', 'again', 'is', 'there', 'anyone', 'who', 'loves', 'or', 'pursues', 'or', 'desires', 'to', 'obtain', 'pain', 'of', 'itself,', 'because', 'it', 'is', 'pain,', 'but', 'because', 'occasionally', 'circumstances', 'occur', 'in', 'which', 'toil', 'and', 'pain', 'can', 'procure', 'him', 'some', 'great', 'pleasure.', 'To', 'take', 'a', 'trivial', 'example,', 'which', 'of', 'us', 'ever', 'undertakes', 'laborious', 'physical', 'exercise,', 'except', 'to', 'obtain', 'some', 'advantage', 'from', 'it?', 'But', 'who', 'has', 'any', 'right', 'to', 'find', 'fault', 'with', 'a', 'man', 'who', 'chooses', 'to', 'enjoy', 'a', 'pleasure', 'that', 'has', 'no', 'annoying', 'consequences,', 'or', 'one', 'who', 'avoids', 'a', 'pain', 'that', 'produces', 'no', 'resultant', 'pleasure?', 'On', 'the', 'other', 'hand,', 'we', 'denounce', 'with', 'righteous', 'indignation', 'and', 'dislike', 'men', 'who', 'are', 'so', 'beguiled', 'and', 'demoralized', 'by', 'the', 'charms', 'of', 'pleasure', 'of', 'the', 'moment,', 'so', 'blinded', 'by', 'desire,', 'that', 'they', 'cannot', 'foresee', 'the', 'pain', 'and', 'trouble', 'that', 'are', 'bound', 'to', 'ensue;', 'and', 'equal', 'blame', 'belongs', 'to', 'those', 'who', 'fail', 'in', 'their', 'duty', 'through', 'weakness', 'of', 'will,', 'which', 'is', 'the', 'same', 'as', 'saying', 'through', 'shrinking', 'from', 'toil', 'and', 'pain.', 'These', 'cases', 'are', 'perfectly', 'simple', 'and', 'easy', 'to', 'distinguish.', 'In', 'a', 'free', 'hour,', 'when', 'our', 'power', 'of', 'choice', 'is', 'untrammelled', 'and', 'when', 'nothing', 'prevents', 'our', 'being', 'able', 'to', 'do', 'what', 'we', 'like', 'best,', 'every', 'pleasure', 'is', 'to', 'be', 'welcomed', 'and', 'every', 'pain', 'avoided.', 'But', 'in', 'certain', 'circumstances', 'and', 'owing', 'to', 'the', 'claims', 'of', 'duty', 'or', 'the', 'obligations', 'of', 'business', 'it', 'will', 'frequently', 'occur', 'that', 'pleasures', 'have', 'to', 'be', 'repudiated', 'and', 'annoyances', 'accepted.', 'The', 'wise', 'man', 'therefore', 'always', 'holds', 'in', 'these', 'matters', 'to', 'this', 'principle', 'of', 'selection:', 'he', 'rejects', 'pleasures', 'to', 'secure', 'other', 'greater', 'pleasures,', 'or', 'else', 'he', 'endures', 'pains', 'to', 'avoid', 'worse', 'pains.But', 'I', 'must', 'explain', 'to', 'you', 'how', 'all', 'this', 'mistaken', 'idea', 'of', 'denouncing', 'pleasure', 'and', 'praising', 'pain', 'was', 'born', 'and', 'I', 'will', 'give', 'you', 'a', 'complete', 'account', 'of', 'the', 'system,', 'and', 'expound', 'the', 'actual', 'teachings', 'of', 'the', 'great', 'explorer', 'of', 'the', 'truth,', 'the', 'master-builder', 'of', 'human', 'happiness.', 'No', 'one', 'rejects,', 'dislikes,', 'or', 'avoids', 'pleasure', 'itself,', 'because', 'it', 'is', 'pleasure,', 'but', 'because', 'those', 'who', 'do', 'not', 'know', 'how', 'to', 'pursue', 'pleasure', 'rationally', 'encounter', 'consequences', 'that', 'are', 'extremely', 'painful.', 'Nor', 'again', 'is', 'there', 'anyone', 'who', 'loves', 'or', 'pursues', 'or', 'desires', 'to', 'obtain', 'pain', 'of', 'itself,', 'because', 'it', 'is', 'pain,', 'but', 'because', 'occasionally', 'circumstances', 'occur', 'in', 'which', 'toil', 'and', 'pain', 'can', 'procure', 'him', 'some', 'great', 'pleasure.', 'To', 'take', 'a', 'trivial', 'example,', 'which', 'of', 'us', 'ever', 'undertakes', 'laborious', 'physical', 'exercise,', 'except', 'to', 'obtain', 'some', 'advantage', 'from', 'it?', 'But', 'who', 'has', 'any', 'right', 'to', 'find', 'fault', 'with', 'a', 'man', 'who', 'chooses', 'to', 'enjoy', 'a', 'pleasure', 'that', 'has', 'no', 'annoying', 'consequences,', 'or', 'one', 'who', 'avoids', 'a', 'pain', 'that', 'produces', 'no', 'resultant', 'pleasure?', 'On', 'the', 'other', 'hand,', 'we', 'denounce', 'with', 'righteous', 'indignation', 'and', 'dislike', 'men', 'who', 'are', 'so', 'beguiled', 'and', 'demoralized', 'by', 'the', 'charms', 'of', 'pleasure', 'of', 'the', 'moment,', 'so', 'blinded', 'by', 'desire,', 'that', 'they', 'cannot', 'foresee', 'the', 'pain', 'and', 'trouble', 'that', 'are', 'bound', 'to', 'ensue;', 'and', 'equal', 'blame', 'belongs', 'to', 'those', 'who', 'fail', 'in', 'their', 'duty', 'through', 'weakness', 'of', 'will,', 'which', 'is', 'the', 'same', 'as', 'saying', 'through', 'shrinking', 'from', 'toil', 'and', 'pain.', 'These', 'cases', 'are', 'perfectly', 'simple', 'and', 'easy', 'to', 'distinguish.', 'In', 'a', 'free', 'hour,', 'when', 'our', 'power', 'of', 'choice', 'is', 'untrammelled', 'and', 'when', 'nothing', 'prevents', 'our', 'being', 'able', 'to', 'do', 'what', 'we', 'like', 'best,', 'every', 'pleasure', 'is', 'to', 'be', 'welcomed', 'and', 'every', 'pain', 'avoided.', 'But', 'in', 'certain', 'circumstances', 'and', 'owing', 'to', 'the', 'claims', 'of', 'duty', 'or', 'the', 'obligations', 'of', 'business', 'it', 'will', 'frequently', 'occur', 'that', 'pleasures', 'have', 'to', 'be', 'repudiated', 'and', 'annoyances', 'accepted.', 'The', 'wise', 'man', 'therefore', 'always', 'holds', 'in', 'these', 'matters', 'to', 'this', 'principle', 'of', 'selection:')

fn corpus3() -> List[String]:
    return List[String]('A', 'wonderful', 'serenity', 'has', 'taken', 'possession', 'of', 'my', 'entire', 'soul,', 'like', 'these', 'sweet', 'mornings', 'of', 'spring', 'which', 'I', 'enjoy', 'with', 'my', 'whole', 'heart.', 'I', 'am', 'alone,', 'and', 'feel', 'the', 'charm', 'of', 'existence', 'in', 'this', 'spot,', 'which', 'was', 'created', 'for', 'the', 'bliss', 'of', 'souls', 'like', 'mine.', 'I', 'am', 'so', 'happy,', 'my', 'dear', 'friend,', 'so', 'absorbed', 'in', 'the', 'exquisite', 'sense', 'of', 'mere', 'tranquil', 'existence,', 'that', 'I', 'neglect', 'my', 'talents.', 'I', 'should', 'be', 'incapable', 'of', 'drawing', 'a', 'single', 'stroke', 'at', 'the', 'present', 'moment;', 'and', 'yet', 'I', 'feel', 'that', 'I', 'never', 'was', 'a', 'greater', 'artist', 'than', 'now.', 'When,', 'while', 'the', 'lovely', 'valley', 'teems', 'with', 'vapour', 'around', 'me,', 'and', 'the', 'meridian', 'sun', 'strikes', 'the', 'upper', 'surface', 'of', 'the', 'impenetrable', 'foliage', 'of', 'my', 'trees,', 'and', 'but', 'a', 'few', 'stray', 'gleams', 'steal', 'into', 'the', 'inner', 'sanctuary,', 'I', 'throw', 'myself', 'down', 'among', 'the', 'tall', 'grass', 'by', 'the', 'trickling', 'stream;', 'and,', 'as', 'I', 'lie', 'close', 'to', 'the', 'earth,', 'a', 'thousand', 'unknown', 'plants', 'are', 'noticed', 'by', 'me:', 'when', 'I', 'hear', 'the', 'buzz', 'of', 'the', 'little', 'world', 'among', 'the', 'stalks,', 'and', 'grow', 'familiar', 'with', 'the', 'countless', 'indescribable', 'forms', 'of', 'the', 'insects', 'and', 'flies,', 'then', 'I', 'feel', 'the', 'presence', 'of', 'the', 'Almighty,', 'who', 'formed', 'us', 'in', 'his', 'own', 'image,', 'and', 'the', 'breath', 'of', 'that', 'universal', 'love', 'which', 'bears', 'and', 'sustains', 'us,', 'as', 'it', 'floats', 'around', 'us', 'in', 'an', 'eternity', 'of', 'bliss;', 'and', 'then,', 'my', 'friend,', 'when', 'darkness', 'overspreads', 'my', 'eyes,', 'and', 'heaven', 'and', 'earth', 'seem', 'to', 'dwell', 'in', 'my', 'soul', 'and', 'absorb', 'its', 'power,', 'like', 'the', 'form', 'of', 'a', 'beloved', 'mistress,', 'then', 'I', 'often', 'think', 'with', 'longing,', 'Oh,', 'would', 'I', 'could', 'describe', 'these', 'conceptions,', 'could', 'impress', 'upon', 'paper', 'all', 'that', 'is', 'living', 'so', 'full', 'and', 'warm', 'within', 'me,', 'that', 'it', 'might', 'be', 'the', 'mirror', 'of', 'my', 'soul,', 'as', 'my', 'soul', 'is', 'the', 'mirror', 'of', 'the', 'infinite', 'God!', 'O', 'my', 'friend', '--', 'but', 'it', 'is', 'too', 'much', 'for', 'my', 'strength', '--', 'I', 'sink', 'under', 'the', 'weight', 'of', 'the', 'splendour', 'of', 'these', 'visions!', 'A', 'wonderful', 'serenity', 'has', 'taken', 'possession', 'of', 'my', 'entire', 'soul,', 'like', 'these', 'sweet', 'mornings', 'of', 'spring', 'which', 'I', 'enjoy', 'with', 'my', 'whole', 'heart.', 'I', 'am', 'alone,', 'and', 'feel', 'the', 'charm', 'of', 'existence', 'in', 'this', 'spot,', 'which', 'was', 'created', 'for', 'the', 'bliss', 'of', 'souls', 'like', 'mine.', 'I', 'am', 'so', 'happy,', 'my', 'dear', 'friend,', 'so', 'absorbed', 'in', 'the', 'exquisite', 'sense', 'of', 'mere', 'tranquil', 'existence,', 'that', 'I', 'neglect', 'my', 'talents.', 'I', 'should', 'be', 'incapable', 'of', 'drawing', 'a', 'single', 'stroke', 'at', 'the', 'present', 'moment;', 'and', 'yet', 'I', 'feel', 'that', 'I', 'never', 'was', 'a', 'greater', 'artist', 'than', 'now.', 'When,', 'while', 'the', 'lovely', 'valley', 'teems', 'with', 'vapour', 'around', 'me,', 'and', 'the', 'meridian', 'sun', 'strikes', 'the', 'upper', 'surface', 'of', 'the', 'impenetrable', 'foliage', 'of', 'my', 'trees,', 'and', 'but', 'a', 'few', 'stray', 'gleams', 'steal', 'into', 'the', 'inner', 'sanctuary,', 'I', 'throw', 'myself', 'down', 'among', 'the', 'tall', 'grass', 'by', 'the', 'trickling', 'stream;', 'and,', 'as', 'I', 'lie', 'close', 'to', 'the', 'earth,', 'a', 'thousand', 'unknown', 'plants', 'are', 'noticed', 'by', 'me:', 'when', 'I', 'hear', 'the', 'buzz', 'of', 'the', 'little', 'world', 'among', 'the', 'stalks,', 'and', 'grow', 'familiar', 'with', 'the', 'countless', 'indescribable', 'forms', 'of', 'the', 'insects', 'and', 'flies,', 'then', 'I', 'feel', 'the', 'presence', 'of', 'the', 'Almighty,', 'who', 'formed', 'us', 'in', 'his', 'own', 'image,', 'and', 'the', 'breath', 'of', 'that', 'universal', 'love', 'which', 'bears', 'and', 'sustains', 'us,', 'as', 'it', 'floats', 'around', 'us', 'in', 'an', 'eternity', 'of', 'bliss;', 'and', 'then,', 'my', 'friend,', 'when', 'darkness', 'overspreads', 'my', 'eyes,', 'and', 'heaven', 'and', 'earth', 'seem', 'to', 'dwell', 'in', 'my', 'soul', 'and', 'absorb', 'its', 'power,', 'like', 'the', 'form', 'of', 'a', 'beloved', 'mistress,', 'then', 'I', 'often', 'think', 'with', 'longing,', 'Oh,', 'would', 'I', 'could', 'describe', 'these', 'conceptions,', 'could', 'impress', 'upon', 'paper', 'all', 'that', 'is', 'living', 'so', 'full', 'and', 'warm', 'within', 'me,', 'that', 'it', 'might', 'be', 'the', 'mirror', 'of', 'my', 'soul,', 'as', 'my', 'soul', 'is', 'the', 'mirror', 'of', 'the', 'infinite', 'God!', 'O', 'my', 'friend', '--', 'but', 'it', 'is', 'too', 'much', 'for', 'my', 'strength', '--', 'I', 'sink', 'under', 'the', 'weight', 'of', 'the', 'splendour', 'of', 'these', 'visions!', 'A', 'wonderful', 'serenity', 'has', 'taken', 'possession', 'of', 'my', 'entire', 'soul,', 'like', 'these', 'sweet', 'mornings', 'of', 'spring', 'which', 'I', 'enjoy', 'with', 'my', 'whole', 'heart.', 'I', 'am', 'alone,', 'and', 'feel', 'the', 'charm', 'of', 'existence', 'in', 'this', 'spot,', 'which', 'was', 'created', 'for', 'the', 'bliss', 'of', 'souls', 'like', 'mine.', 'I', 'am', 'so', 'happy,', 'my', 'dear', 'friend,', 'so', 'absorbed', 'in', 'the', 'exquisite', 'sense', 'of', 'mere', 'tranquil', 'existence,', 'that', 'I', 'neglect', 'my', 'talents.', 'I', 'should', 'be', 'incapable', 'of', 'drawing', 'a', 'single', 'stroke', 'at', 'the', 'present', 'moment;', 'and', 'yet', 'I', 'feel', 'that', 'I', 'never', 'was', 'a', 'greater', 'artist', 'than', 'now.', 'When,', 'while', 'the', 'lovely', 'valley', 'teems', 'with', 'vapour', 'around', 'me,', 'and', 'the', 'meridian', 'sun', 'strikes', 'the', 'upper', 'surface', 'of', 'the', 'impenetrable', 'foliage', 'of', 'my', 'trees,', 'and', 'but', 'a', 'few', 'stray', 'gleams', 'steal', 'into', 'the', 'inner', 'sanctuary,', 'I', 'throw', 'myself', 'down', 'among', 'the', 'tall', 'grass', 'by', 'the', 'trickling', 'stream;', 'and,', 'as', 'I', 'lie', 'close', 'to', 'the', 'earth,', 'a', 'thousand', 'unknown', 'plants', 'are', 'noticed', 'by', 'me:', 'when', 'I', 'hear', 'the', 'buzz', 'of', 'the', 'little', 'world', 'among', 'the', 'stalks,', 'and', 'grow', 'familiar', 'with', 'the', 'countless', 'indescribable', 'forms', 'of', 'the', 'insects', 'and', 'flies,', 'then', 'I', 'feel', 'the', 'presence', 'of', 'the', 'Almighty,', 'who', 'formed', 'us', 'in', 'his', 'own', 'image,', 'and', 'the', 'breath', 'of', 'that', 'universal', 'love', 'which', 'bears', 'and', 'sustains', 'us,', 'as', 'it', 'floats', 'around', 'us', 'in', 'an', 'eternity', 'of', 'bliss;', 'and', 'then,', 'my', 'friend,', 'when', 'darkness', 'overspreads', 'my', 'eyes,', 'and', 'heaven', 'and', 'earth', 'seem', 'to', 'dwell', 'in', 'my', 'soul', 'and', 'absorb', 'its', 'power,', 'like', 'the', 'form', 'of', 'a', 'beloved', 'mistress,', 'then', 'I', 'often', 'think', 'with', 'longing,', 'Oh,', 'would', 'I', 'could', 'describe', 'these', 'conceptions,', 'could', 'impress', 'upon', 'paper', 'all', 'that', 'is', 'living', 'so', 'full', 'and', 'warm', 'within', 'me,', 'that', 'it', 'might', 'be', 'the', 'mirror', 'of', 'my', 'soul,', 'as', 'my', 'soul', 'is', 'the', 'mirror', 'of', 'the', 'infinite', 'God!', 'O', 'my', 'friend', '--', 'but', 'it', 'is', 'too', 'much', 'for', 'my', 'strength', '--', 'I', 'sink', 'under', 'the', 'weight', 'of', 'the', 'splendour', 'of', 'these', 'visions!A', 'wonderful', 'serenity', 'has', 'taken', 'possession', 'of', 'my', 'entire', 'soul,', 'like', 'these', 'sweet', 'mornings', 'of', 'spring', 'which', 'I', 'enjoy', 'with', 'my', 'whole', 'heart.', 'I', 'am', 'alone,', 'and', 'feel', 'the', 'charm', 'of', 'existence', 'in', 'this', 'spot,', 'which', 'was', 'created', 'for', 'the', 'bliss', 'of', 'souls')

fn corpus4() -> List[String]:
    return List[String]('Проснувшись', 'однажды', 'утром', 'после', 'беспокойного', 'сна,', 'Грегор', 'Замза', 'обнаружил,', 'что', 'он', 'у', 'себя', 'в', 'постели', 'превратился', 'в', 'страшное', 'насекомое.', 'Лежа', 'на', 'панцирнотвердой', 'спине,', 'он', 'видел,', 'стоило', 'ему', 'приподнять', 'голову,', 'свой', 'коричневый,', 'выпуклый,', 'разделенный', 'дугообразными', 'чешуйками', 'живот,', 'на', 'верхушке', 'которого', 'еле', 'держалось', 'готовое', 'вот-вот', 'окончательно', 'сползти', 'одеяло.', 'Его', 'многочисленные,', 'убого', 'тонкие', 'по', 'сравнению', 'с', 'остальным', 'телом', 'ножки', 'беспомощно', 'копошились', 'у', 'него', 'перед', 'глазами.', '«Что', 'со', 'мной', 'случилось?»', '–', 'подумал', 'он.', 'Это', 'не', 'было', 'сном.', 'Его', 'комната,', 'настоящая,', 'разве', 'что', 'слишком', 'маленькая,', 'но', 'обычная', 'комната,', 'мирно', 'покоилась', 'в', 'своих', 'четырех', 'хорошо', 'знакомых', 'стенах.', 'Над', 'столом,', 'где', 'были', 'разложены', 'распакованные', 'образцы', 'сукон', '–', 'Замза', 'был', 'коммивояжером,', '–', 'висел', 'портрет,', 'который', 'он', 'недавно', 'вырезал', 'из', 'иллюстрированного', 'журнала', 'и', 'вставил', 'в', 'красивую', 'золоченую', 'рамку.', 'На', 'портрете', 'была', 'изображена', 'дама', 'в', 'меховой', 'шляпе', 'и', 'боа,', 'она', 'сидела', 'очень', 'прямо', 'и', 'протягивала', 'зрителю', 'тяжелую', 'меховую', 'муфту,', 'в', 'которой', 'целиком', 'исчезала', 'ее', 'рука.', 'Затем', 'взгляд', 'Грегора', 'устремился', 'в', 'окно,', 'и', 'пасмурная', 'погода', '–', 'слышно', 'было,', 'как', 'по', 'жести', 'подоконника', 'стучат', 'капли', 'дождя', '–', 'привела', 'его', 'и', 'вовсе', 'в', 'грустное', 'настроение.', '«Хорошо', 'бы', 'еще', 'немного', 'поспать', 'и', 'забыть', 'всю', 'эту', 'чепуху»,', '–', 'подумал', 'он,', 'но', 'это', 'было', 'совершенно', 'неосуществимо,', 'он', 'привык', 'спать', 'на', 'правом', 'боку,', 'а', 'в', 'теперешнем', 'своем', 'состоянии', 'он', 'никак', 'не', 'мог', 'принять', 'этого', 'положения.', 'С', 'какой', 'бы', 'силой', 'ни', 'поворачивался', 'он', 'на', 'правый', 'бок,', 'он', 'неизменно', 'сваливался', 'опять', 'на', 'спину.', 'Закрыв', 'глаза,', 'чтобы', 'не', 'видеть', 'своих', 'барахтающихся', 'ног,', 'он', 'проделал', 'это', 'добрую', 'сотню', 'раз', 'и', 'отказался', 'от', 'этих', 'попыток', 'только', 'тогда,', 'когда', 'почувствовал', 'какую-то', 'неведомую', 'дотоле,', 'тупую', 'и', 'слабую', 'боль', 'в', 'боку.', '«Ах', 'ты,', 'господи,', '–', 'подумал', 'он,', '–', 'какую', 'я', 'выбрал', 'хлопотную', 'профессию!', 'Изо', 'дня', 'в', 'день', 'в', 'разъездах.', 'Деловых', 'волнений', 'куда', 'больше,', 'чем', 'на', 'месте,', 'в', 'торговом', 'доме,', 'а', 'кроме', 'того,', 'изволь', 'терпеть', 'тяготы', 'дороги,', 'думай', 'о', 'расписании', 'поездов,', 'мирись', 'с', 'плохим,', 'нерегулярным', 'питанием,', 'завязывай', 'со', 'все', 'новыми', 'и', 'новыми', 'людьми', 'недолгие,', 'никогда', 'не', 'бывающие', 'сердечными', 'отношения.', 'Черт', 'бы', 'побрал', 'все', 'это!»', 'Он', 'почувствовал', 'вверху', 'живота', 'легкий', 'зуд;', 'медленно', 'подвинулся', 'на', 'спине', 'к', 'прутьям', 'кровати,', 'чтобы', 'удобнее', 'было', 'поднять', 'голову;', 'нашел', 'зудевшее', 'место,', 'сплошь', 'покрытое,', 'как', 'оказалось,', 'белыми', 'непонятными', 'точечками;', 'хотел', 'было', 'ощупать', 'это', 'место', 'одной', 'из', 'ножек,', 'но', 'сразу', 'отдернул', 'ее,', 'ибо', 'даже', 'простое', 'прикосновение', 'вызвало', 'у', 'него,', 'Грегора,', 'озноб.', 'Он', 'соскользнул', 'в', 'прежнее', 'свое', 'положение.', '«От', 'этого', 'раннего', 'вставания,', '–', 'подумал', 'он,', '–', 'можно', 'совсем', 'обезуметь.', 'Человек', 'должен', 'высыпаться.', 'Другие', 'коммивояжеры', 'живут,', 'как', 'одалиски.', 'Когда', 'я,', 'например,', 'среди', 'дня', 'возвращаюсь', 'в', 'гостиницу,', 'чтобы', 'переписать', 'полученные', 'заказы,', 'эти', 'господа', 'только', 'завтракают.', 'А', 'осмелься', 'я', 'вести', 'себя', 'так,', 'мои', 'хозяин', 'выгнал', 'бы', 'меня', 'сразу.', 'Кто', 'знает,', 'впрочем,', 'может', 'быть,', 'это', 'было', 'бы', 'даже', 'очень', 'хорошо', 'для', 'меня.', 'Если', 'бы', 'я', 'не', 'сдерживался', 'ради', 'родителей,', 'я', 'бы', 'давно', 'заявил', 'об', 'уходе,', 'я', 'бы', 'подошел', 'к', 'своему', 'хозяину', 'и', 'выложил', 'ему', 'все,', 'что', 'о', 'нем', 'думаю.', 'Он', 'бы', 'так', 'и', 'свалился', 'с', 'конторки!', 'Странная', 'у', 'него', 'манера', '–', 'садиться', 'на', 'конторку', 'и', 'с', 'ее', 'высоты', 'разговаривать', 'со', 'служащим,', 'который', 'вдобавок', 'вынужден', 'подойти', 'вплотную', 'к', 'конторке', 'из-за', 'того,', 'что', 'хозяин', 'туг', 'на', 'ухо.', 'Однако', 'надежда', 'еще', 'не', 'совсем', 'потеряна:', 'как', 'только', 'я', 'накоплю', 'денег,', 'чтобы', 'выплатить', 'долг', 'моих', 'родителей', '–', 'на', 'это', 'уйдет', 'еще', 'лет', 'пять-шесть,', '–', 'я', 'так', 'и', 'поступлю.', 'Тут-то', 'мы', 'и', 'распрощаемся', 'раз', 'и', 'навсегда.', 'А', 'пока', 'что', 'надо', 'подниматься,', 'мой', 'поезд', 'отходит', 'в', 'пять».', 'И', 'он', 'взглянул', 'на', 'будильник,', 'который', 'тикал', 'на', 'сундуке.', '«Боже', 'правый!»', '–', 'подумал', 'он.', 'Было', 'половина', 'седьмого,', 'и', 'стрелки', 'спокойно', 'двигались', 'дальше,', 'было', 'даже', 'больше', 'половины,', 'без', 'малого', 'уже', 'три', 'четверти.', 'Неужели', 'будильник', 'не', 'звонил?', 'С', 'кровати', 'было', 'видно,', 'что', 'он', 'поставлен', 'правильно,', 'на', 'четыре', 'часа;', 'и', 'он,', 'несомненно,', 'звонил.', 'Но', 'как', 'можно', 'было', 'спокойно', 'спать', 'под', 'этот', 'сотрясающий', 'мебель', 'трезвон?', 'Ну,', 'спал-то', 'он', 'неспокойно,', 'но,', 'видимо,', 'крепко.', 'Однако', 'что', 'делать', 'теперь?', 'Следующий', 'поезд', 'уходит', 'в', 'семь', 'часов;', 'чтобы', 'поспеть', 'на', 'него,', 'он', 'должен', 'отчаянно', 'торопиться,', 'а', 'набор', 'образцов', 'еще', 'не', 'упакован,', 'да', 'и', 'сам', 'он', 'отнюдь', 'не', 'чувствует', 'себя', 'свежим', 'и', 'легким', 'на', 'подъем.', 'И', 'даже', 'поспей', 'он', 'на', 'поезд,', 'хозяйского', 'разноса', 'ему', 'все', 'равно', 'не', 'избежать', '–', 'ведь', 'рассыльный', 'торгового', 'дома', 'дежурил', 'у', 'пятичасового', 'поезда', 'и', 'давно', 'доложил', 'о', 'его,', 'Грегора,', 'опоздании.', 'Рассыльный,', 'человек', 'бесхарактерный', 'и', 'неумный,', 'был', 'ставленником', 'хозяина.', 'А', 'что,', 'если', 'сказаться', 'больным?', 'Но', 'это', 'было', 'бы', 'крайне', 'неприятно', 'и', 'показалось', 'бы', 'подозрительным,', 'ибо', 'за', 'пятилетнюю', 'свою', 'службу', 'Грегор', 'ни', 'разу', 'еще', 'не', 'болел.', 'Хозяин,', 'конечно,', 'привел', 'бы', 'врача', 'больничной', 'кассы', 'и', 'стал', 'попрекать', 'родителей', 'сыном-лентяем,', 'отводя', 'любые', 'возражения', 'ссылкой', 'на', 'этого', 'врача,', 'по', 'мнению', 'которого', 'все', 'люди', 'на', 'свете', 'совершенно', 'здоровы', 'и', 'только', 'не', 'любят', 'работать.', 'И', 'разве', 'в', 'данном', 'случае', 'он', 'был', 'бы', 'так', 'уж', 'неправ?', 'Если', 'не', 'считать', 'сонливости,', 'действительно', 'странной', 'после', 'такого', 'долгого', 'сна,', 'Грегор', 'и', 'в', 'самом', 'деле', 'чувствовал', 'себя', 'превосходно', 'и', 'был', 'даже', 'чертовски', 'голоден.Проснувшись', 'однажды', 'утром', 'после', 'беспокойного', 'сна,', 'Грегор', 'Замза', 'обнаружил,', 'что', 'он', 'у', 'себя', 'в', 'постели', 'превратился', 'в', 'страшное', 'насекомое.', 'Лежа', 'на', 'панцирнотвердой', 'спине,', 'он', 'видел,', 'стоило', 'ему', 'приподнять', 'голову,', 'свой', 'коричневый,', 'выпуклый,', 'разделенный', 'дугообразными', 'чешуйками', 'живот,', 'на', 'верхушке', 'которого', 'еле', 'держалось', 'готовое', 'вот-вот', 'окончательно', 'сползти', 'одеяло.', 'Его', 'многочисленные,', 'убого', 'тонкие', 'по', 'сравнению', 'с', 'остальным', 'телом', 'ножки', 'беспомощно', 'копошились', 'у', 'него', 'перед', 'глазами.', '«Что', 'со', 'мной', 'случилось?»', '–', 'подумал', 'он.', 'Это', 'не', 'было', 'сном.', 'Его', 'комната,', 'настоящая,', 'разве', 'что', 'слишком', 'маленькая,', 'но', 'обычная', 'комната,', 'мирно', 'покоилась', 'в', 'своих', 'четырех', 'хорошо', 'знакомых', 'стенах.', 'Над', 'столом,', 'где', 'были', 'разложены', 'распакованные', 'образцы', 'сукон', '–', 'Замза', 'был', 'коммивояжером,', '–', 'висел', 'портрет,', 'который', 'он', 'недавно', 'вырезал', 'из', 'иллюстрированного', 'журнала', 'и', 'вставил', 'в', 'красивую', 'золоченую', 'рамку.', 'На', 'портрете', 'была', 'изображена', 'дама', 'в', 'меховой', 'шляпе', 'и', 'боа,', 'она', 'сидела', 'очень', 'прямо', 'и', 'протягивала', 'зрителю', 'тяжелую', 'меховую', 'муфту,', 'в', 'которой', 'целиком', 'исчезала', 'ее', 'рука.', 'Затем', 'взгляд', 'Грегора', 'устремился', 'в', 'окно,', 'и', 'пасмурная', 'погода', '–', 'слышно', 'было,', 'как', 'по', 'жести', 'подоконника', 'стучат', 'капли', 'дождя', '–', 'привела', 'его', 'и', 'вовсе', 'в', 'грустное', 'настроение.', '«Хорошо', 'бы', 'еще', 'немного', 'поспать', 'и', 'забыть', 'всю', 'эту', 'чепуху»,', '–', 'подумал', 'он,', 'но', 'это', 'было', 'совершенно', 'неосуществимо,', 'он', 'привык', 'спать', 'на', 'правом', 'боку,', 'а', 'в', 'теперешнем', 'своем', 'состоянии', 'он', 'никак', 'не', 'мог', 'принять', 'этого', 'положения.', 'С', 'какой', 'бы', 'силой', 'ни', 'поворачивался', 'он', 'на', 'правый', 'бок,', 'он', 'неизменно', 'сваливался', 'опять', 'на', 'спину.')

fn corpus5() -> List[String]:
    return List[String]('Weit', 'hinten,', 'hinter', 'den', 'Wortbergen,', 'fern', 'der', 'Länder', 'Vokalien', 'und', 'Konsonantien', 'leben', 'die', 'Blindtexte.', 'Abgeschieden', 'wohnen', 'sie', 'in', 'Buchstabhausen', 'an', 'der', 'Küste', 'des', 'Semantik,', 'eines', 'großen', 'Sprachozeans.', 'Ein', 'kleines', 'Bächlein', 'namens', 'Duden', 'fließt', 'durch', 'ihren', 'Ort', 'und', 'versorgt', 'sie', 'mit', 'den', 'nötigen', 'Regelialien.', 'Es', 'ist', 'ein', 'paradiesmatisches', 'Land,', 'in', 'dem', 'einem', 'gebratene', 'Satzteile', 'in', 'den', 'Mund', 'fliegen.', 'Nicht', 'einmal', 'von', 'der', 'allmächtigen', 'Interpunktion', 'werden', 'die', 'Blindtexte', 'beherrscht', '–', 'ein', 'geradezu', 'unorthographisches', 'Leben.', 'Eines', 'Tages', 'aber', 'beschloß', 'eine', 'kleine', 'Zeile', 'Blindtext,', 'ihr', 'Name', 'war', 'Lorem', 'Ipsum,', 'hinaus', 'zu', 'gehen', 'in', 'die', 'weite', 'Grammatik.', 'Der', 'große', 'Oxmox', 'riet', 'ihr', 'davon', 'ab,', 'da', 'es', 'dort', 'wimmele', 'von', 'bösen', 'Kommata,', 'wilden', 'Fragezeichen', 'und', 'hinterhältigen', 'Semikoli,', 'doch', 'das', 'Blindtextchen', 'ließ', 'sich', 'nicht', 'beirren.', 'Es', 'packte', 'seine', 'sieben', 'Versalien,', 'schob', 'sich', 'sein', 'Initial', 'in', 'den', 'Gürtel', 'und', 'machte', 'sich', 'auf', 'den', 'Weg.', 'Als', 'es', 'die', 'ersten', 'Hügel', 'des', 'Kursivgebirges', 'erklommen', 'hatte,', 'warf', 'es', 'einen', 'letzten', 'Blick', 'zurück', 'auf', 'die', 'Skyline', 'seiner', 'Heimatstadt', 'Buchstabhausen,', 'die', 'Headline', 'von', 'Alphabetdorf', 'und', 'die', 'Subline', 'seiner', 'eigenen', 'Straße,', 'der', 'Zeilengasse.', 'Wehmütig', 'lief', 'ihm', 'eine', 'rhetorische', 'Frage', 'über', 'die', 'Wange,', 'dann', 'setzte', 'es', 'seinen', 'Weg', 'fort.', 'Unterwegs', 'traf', 'es', 'eine', 'Copy.', 'Die', 'Copy', 'warnte', 'das', 'Blindtextchen,', 'da,', 'wo', 'sie', 'herkäme', 'wäre', 'sie', 'zigmal', 'umgeschrieben', 'worden', 'und', 'alles,', 'was', 'von', 'ihrem', 'Ursprung', 'noch', 'übrig', 'wäre,', 'sei', 'das', 'Wort', '"und"', 'und', 'das', 'Blindtextchen', 'solle', 'umkehren', 'und', 'wieder', 'in', 'sein', 'eigenes,', 'sicheres', 'Land', 'zurückkehren.', 'Doch', 'alles', 'Gutzureden', 'konnte', 'es', 'nicht', 'überzeugen', 'und', 'so', 'dauerte', 'es', 'nicht', 'lange,', 'bis', 'ihm', 'ein', 'paar', 'heimtückische', 'Werbetexter', 'auflauerten,', 'es', 'mit', 'Longe', 'und', 'Parole', 'betrunken', 'machten', 'und', 'es', 'dann', 'in', 'ihre', 'Agentur', 'schleppten,', 'wo', 'sie', 'es', 'für', 'ihre', 'Projekte', 'wieder', 'und', 'wieder', 'mißbrauchten.', 'Und', 'wenn', 'es', 'nicht', 'umgeschrieben', 'wurde,', 'dann', 'benutzen', 'Sie', 'es', 'immernoch.', 'Weit', 'hinten,', 'hinter', 'den', 'Wortbergen,', 'fern', 'der', 'Länder', 'Vokalien', 'und', 'Konsonantien', 'leben', 'die', 'Blindtexte.', 'Abgeschieden', 'wohnen', 'sie', 'in', 'Buchstabhausen', 'an', 'der', 'Küste', 'des', 'Semantik,', 'eines', 'großen', 'Sprachozeans.', 'Ein', 'kleines', 'Bächlein', 'namens', 'Duden', 'fließt', 'durch', 'ihren', 'Ort', 'und', 'versorgt', 'sie', 'mit', 'den', 'nötigen', 'Regelialien.', 'Es', 'ist', 'ein', 'paradiesmatisches', 'Land,', 'in', 'dem', 'einem', 'gebratene', 'Satzteile', 'in', 'den', 'Mund', 'fliegen.', 'Nicht', 'einmal', 'von', 'der', 'allmächtigen', 'Interpunktion', 'werden', 'die', 'Blindtexte', 'beherrscht', '–', 'ein', 'geradezu', 'unorthographisches', 'Leben.', 'Eines', 'Tages', 'aber', 'beschloß', 'eine', 'kleine', 'Zeile', 'Blindtext,', 'ihr', 'Name', 'war', 'Lorem', 'Ipsum,', 'hinaus', 'zu', 'gehen', 'in', 'die', 'weite', 'Grammatik.', 'Der', 'große', 'Oxmox', 'riet', 'ihr', 'davon', 'ab,', 'da', 'es', 'dort', 'wimmele', 'von', 'bösen', 'Kommata,', 'wilden', 'Fragezeichen', 'und', 'hinterhältigen', 'Semikoli,', 'doch', 'das', 'Blindtextchen', 'ließ', 'sich', 'nicht', 'beirren.', 'Es', 'packte', 'seine', 'sieben', 'Versalien,', 'schob', 'sich', 'sein', 'Initial', 'in', 'den', 'Gürtel', 'und', 'machte', 'sich', 'auf', 'den', 'Weg.', 'Als', 'es', 'die', 'ersten', 'Hügel', 'des', 'Kursivgebirges', 'erklommen', 'hatte,', 'warf', 'es', 'einen', 'letzten', 'Blick', 'zurück', 'auf', 'die', 'Skyline', 'seiner', 'Heimatstadt', 'Buchstabhausen,', 'die', 'Headline', 'von', 'Alphabetdorf', 'und', 'die', 'Subline', 'seiner', 'eigenen', 'Straße,', 'der', 'Zeilengasse.', 'Wehmütig', 'lief', 'ihm', 'eine', 'rhetorische', 'Frage', 'über', 'die', 'Wange,', 'dann', 'setzte', 'es', 'seinen', 'Weg', 'fort.', 'Unterwegs', 'traf', 'es', 'eine', 'Copy.', 'Die', 'Copy', 'warnte', 'das', 'Blindtextchen,', 'da,', 'wo', 'sie', 'herkäme', 'wäre', 'sie', 'zigmal', 'umgeschrieben', 'worden', 'und', 'alles,', 'was', 'von', 'ihrem', 'Ursprung', 'noch', 'übrig', 'wäre,', 'sei', 'das', 'Wort', '"und"', 'und', 'das', 'Blindtextchen', 'solle', 'umkehren', 'und', 'wieder', 'in', 'sein', 'eigenes,', 'sicheres', 'Land', 'zurückkehren.', 'Doch', 'alles', 'Gutzureden', 'konnte', 'es', 'nicht', 'überzeugen', 'und', 'so', 'dauerte', 'es', 'nicht', 'lange,', 'bis', 'ihm', 'ein', 'paar', 'heimtückische', 'Werbetexter', 'auflauerten,', 'es', 'mit', 'Longe', 'und', 'Parole', 'betrunken', 'machten', 'und', 'es', 'dann', 'in', 'ihre', 'Agentur', 'schleppten,', 'wo', 'sie', 'es', 'für', 'ihre', 'Projekte', 'wieder', 'und', 'wieder', 'mißbrauchten.', 'Und', 'wenn', 'es', 'nicht', 'umgeschrieben', 'wurde,', 'dann', 'benutzen', 'Sie', 'es', 'immernoch.', 'Weit', 'hinten,', 'hinter', 'den', 'Wortbergen,', 'fern', 'der', 'Länder', 'Vokalien', 'und', 'Konsonantien', 'leben', 'die', 'Blindtexte.', 'Abgeschieden', 'wohnen', 'sie', 'in', 'Buchstabhausen', 'an', 'der', 'Küste', 'des', 'Semantik,', 'eines', 'großen', 'Sprachozeans.', 'Ein', 'kleines', 'Bächlein', 'namens', 'Duden', 'fließt', 'durch', 'ihren', 'Ort', 'und', 'versorgt', 'sie', 'mit', 'den', 'nötigen', 'Regelialien.', 'Es', 'ist', 'ein', 'paradiesmatisches', 'Land,', 'in', 'dem', 'einem', 'gebratene', 'Satzteile', 'in', 'den', 'Mund', 'fliegen.', 'Nicht', 'einmal', 'von', 'der', 'allmächtigen', 'Interpunktion', 'werden', 'die', 'Blindtexte', 'beherrscht', '–', 'ein', 'geradezu', 'unorthographisches', 'Leben.', 'Eines', 'Tages', 'aber', 'beschloß', 'eine', 'kleine', 'Zeile', 'Blindtext,', 'ihr', 'Name', 'war', 'Lorem', 'Ipsum,', 'hinaus', 'zu', 'gehen', 'in', 'die', 'weite', 'Grammatik.', 'Der', 'große', 'Oxmox', 'riet', 'ihr', 'davon', 'ab,', 'da', 'es', 'dort', 'wimmele', 'von', 'bösen', 'Kommata,', 'wilden', 'Fragezeichen', 'und', 'hinterhältigen', 'Semikoli,', 'doch', 'das', 'Blindtextchen', 'ließ', 'sich', 'nicht', 'beirren.', 'Es', 'packte', 'seine', 'sieben', 'Versalien,', 'schob', 'sich', 'sein', 'Initial', 'in', 'den', 'Gürtel', 'und', 'machte', 'sich', 'auf', 'den', 'Weg.', 'Als', 'es', 'die', 'ersten', 'Hügel', 'des', 'Kursivgebirges', 'erklommen', 'hatte,', 'warf', 'es', 'einen', 'letzten', 'Blick', 'zurück', 'auf', 'die', 'Skyline', 'seiner', 'Heimatstadt', 'Buchstabhausen,', 'die', 'Headline', 'von', 'Alphabetdorf', 'und', 'die', 'Subline', 'seiner', 'eigenen', 'Straße,', 'der', 'Zeilengasse.', 'Wehmütig', 'lief', 'ihm', 'eine', 'rhetorische', 'Frage', 'über', 'die', 'Wange,', 'dann', 'setzte', 'es', 'seinen', 'Weg', 'fort.', 'Unterwegs', 'traf', 'es', 'eine', 'Copy.', 'Die', 'Copy', 'warnte', 'das', 'Blindtextchen,', 'da,', 'wo', 'sie', 'herkäme', 'wäre', 'sie', 'zigmal', 'umgeschrieben', 'worden', 'und', 'alles,', 'was', 'von', 'ihrem', 'Ursprung', 'noch', 'übrig', 'wäre,', 'sei', 'das', 'Wort', '"und"', 'und', 'das', 'Blindtextchen', 'solle', 'umkehren', 'und', 'wieder', 'in', 'sein', 'eigenes,', 'sicheres', 'Land', 'zurückkehren.', 'Doch', 'alles', 'Gutzureden', 'konnte', 'es', 'nicht', 'überzeugen', 'und', 'so', 'dauerte', 'es', 'nicht', 'lange,', 'bis', 'ihm', 'ein', 'paar', 'heimtückische', 'Werbetexter', 'auflauerten,', 'es', 'mit', 'Longe', 'und', 'Parole', 'betrunken', 'machten', 'und', 'es', 'dann', 'in', 'ihre', 'Agentur', 'schleppten,', 'wo', 'sie', 'es', 'für', 'ihre', 'Projekte', 'wieder', 'und', 'wieder', 'mißbrauchten.', 'Und', 'wenn', 'es', 'nicht', 'umgeschrieben', 'wurde,', 'dann', 'benutzen', 'Sie', 'es', 'immernoch.Weit', 'hinten,', 'hinter', 'den', 'Wortbergen,', 'fern', 'der', 'Länder', 'Vokalien', 'und', 'Konsonantien', 'leben', 'die', 'Blindtexte.', 'Abgeschieden', 'wohnen', 'sie', 'in', 'Buchstabhausen', 'an', 'der', 'Küste', 'des', 'Semantik,', 'eines', 'großen', 'Sprachozeans.', 'Ein', 'kleines', 'Bächlein', 'namens', 'Duden', 'fließt', 'durch', 'ihren', 'Ort', 'und', 'versorgt', 'sie', 'mit', 'den', 'nötigen', 'Regelialien.', 'Es', 'ist', 'ein', 'paradiesmatisches', 'Land,', 'in', 'dem', 'einem', 'gebratene', 'Satzteile', 'in', 'den', 'Mund', 'fliegen.', 'Nicht', 'einmal', 'von', 'der', 'allmächtigen', 'Interpunktion', 'werden', 'die', 'Blindtexte', 'beherrscht', '–', 'ein', 'geradezu', 'unorthographisches', 'Leben.', 'Eines', 'Tages', 'aber', 'beschloß', 'eine', 'kleine', 'Zeile', 'Blindtext,', 'ihr', 'Name', 'war', 'Lorem', 'Ipsum,', 'hinaus', 'zu', 'gehen', 'in', 'die', 'weite', 'Grammatik.', 'Der', 'große', 'Oxmox', 'riet', 'ihr', 'davon', 'ab,', 'da', 'es', 'dort', 'wimmele', 'von', 'bösen', 'Kommata,', 'wilden', 'Fragezeichen', 'und', 'hinterhältigen', 'Semikoli,', 'doch', 'das', 'Blindtextchen', 'ließ', 'sich', 'nicht', 'beirren.', 'Es', 'packte', 'seine', 'sieben', 'Versalien,', 'schob', 'sich', 'sein', 'Initial', 'in', 'den', 'Gürtel', 'und', 'machte', 'sich', 'auf', 'den', 'Weg.', 'Als', 'es', 'die', 'ersten', 'Hügel', 'des', 'Kursivgebirges', 'erklommen', 'hatte,', 'warf', 'es', 'einen')

fn corpus6() -> List[String]:
    return List[String]('米くを舵4物委らご氏松ハナテフ月関ソ時平ふいの博情れじフ牟万い元56園フメヤオ試図ロツヤ未備王こと傷喫羅踊んゆし。栃ユヱオ書著作ユソツロ英祉業ア大課ご権質フべ空8午キ切軟づン著郎そゃす格町採ヱオマコ処8付国ムハチア究表でなだ際無ロミヱ地兵ぴげ庭体すク発抜爆位や。楽富むゆず盛航カナセ携代ハ本高きた員59今骸ンラえぜ城解イケ穴訴ぽぎ属住ヤケトヌ抱点ト広注厚でて。', '国リ出難セユメ軍手ヘカウ画形サヲシ猛85用ヲキミ心死よしと身処ケヨミオ教主ーぽ事業んく字国たさょ図能シミスヤ社8板ル岡世58次戒知院んれり。市メ誘根カ数問禁竹ゃれえみ給辺のでみき今二ぎさ裕止過こクすと無32郎所ラた生展ヌヘス成度慣葬勇厘ばてか。室ゃ下携疲ム色権がぽりっ銃週ノオ姫千テム健蔵い研手ッ放容ル告属め旅側26企サノヨ宅都福ぞ通待ちぴね種脳イど労希望義通むン。', '罰しい続負せ著低たル異師ユハワ東添質コチ転集ルヤ雇聴約ヒ前統らた情厳ゆさでや真胸や有披暑棚豆ゆぼたけ。盛ワセロナ情競クるっわ講3音ずをせ少地めしぜょ手63明視れに判企ヒヌエソ求総58特本ね井比ユラキ禁頭馬るゅリす能率率かがさわ。葉サソ医郡ヱヘソ労帰ナケスミ救写ワヘ株審ネヒニミ安逮イ人画ラ涯車はラ極騒りなド件5級ンかふー劇41著ぱぐ凱討だ文世ぶづどま界善魅マ渓経競融れがや。', '連ーぜらご模分ッ視外ばフく運発群ほぼづ育越一ほごクけ案募ヲイソ治会イせフ製君ぜた漢村1変リヒ構5際ツ御文ヲ臭入さドぼ代書ハケ引技ろみれ回観注倉徹ぱ。論ラづ海要サ情座ゃり齢宣ラモエ芸化エマホ覧催回ら戦69本外ト葬岳な政画か連針ぴリフず。約ル闘辺ぽ経2応掲ホサアラ塾小コラ画決クノオ上室レヌヱ勝逮ぜるえむ責豊チノ明意ひけ訟6碁草メタチエ財午召喝塊む。', '決めでわ名金つけレわ続人県約ぽぼす尾腹ユサ戦載リシ護賀レモフツ重涯ニ治者むんっみ職更カタチレ提話2何ワ責東まけげふ能政ヌ供禁がびてわ提改倶れめ。読み担後ぽ安加ぎ論鹿ツ統最お気麻月つじもあ竜思いろめ判必満理トコ文連ムイウハ寄串ざほびー。文ゆこっ向27年メイ便能ノセヲ待1王スねたゆ伝派んね点過カト治読よにきべ使人スシ都言え阻8割べづえみ注引敷的岳犠眠どそ。', '学用イだ医客開ロ供界もぞだ実隆モイヌ務坂ナコヲ権野ろづ初場ぱ低会づぱじ新倒コ化政レ止奮浸猪ッわえづ。形いやリ要帰ほまむだ業領スル必打さ島14巻リ集日ネヘホタ面幅ち写上そぴ円図ムタコモ報使イわざと会催ヤヲ康証をドぶレ盤岡ホハツ作29管しをめ公問懐蓄っさ。来ゆぼあぱ投秋シ語右ぐ身靖かば辛握捕家記ヘワ神岐囲づ毘観メテクツ政73夕罪57需93誌飲査仁さ。', '変レめ束球よんま会特ヱコ聞重だ史純ーどる件32浦レぴよゃ上強ネラリロ査従セユヤ専棋光レ作表ひぶ予正ぜーな誉確フス函6報円ス進治ね能営済否雄でわょ。42生型ば着続ア短実ぎおめび前環闘ラヤヲル診均っとにの声公トヱテマ整試椅情久妊舌頃ざとっく。品キチトテ阿国ラら受87世ヲフセリ川86個ーょぼげ危子ヘレカメ無会ぱかへ事通んかて電条ロツ徴商ぶぞそを居暑メ害広せもがり禁応レミヲ応響割壮憶はぱ。', '千れンが織財メニ況界ネトレミ学豊フオホシ近月レたやご的罪ょな菱技ちる警栗エセ提89林危氷48参ア説森クキヒヱ薬社ホコエリ負和ルび紀下ケミイ掲歳特ごず扱底ク護木連ちクを各形ばすか。変ぱなれ町7融ヌ街準以タユヘム質裕ぶで遺語俊ぎずょ事金文キ写多山ーゆに歩帯すで会世クぜよ論写ヲ達71林危氷5間続ぎぜび高怠す。', '係8青け応著ミ戦条ナヘネカ思79未ぎ算伊をゃ泉人ーづ需説っ畑鹿27軽ラソツ権2促千護ルロナカ開国ケ暴嶋ご池表だ。佐フナ訪麻はてせば勝効をあ医戦画とさわぴ者両すいあ並来んば載食ぴ件友頂業へえぞ魚祝ネラ聞率スコリケ始全ンこび夫出ドふ今布うぎふゅ実克即哉循やしんな。', '暮す備54依紀てッん末刊と柔称むてス無府ケイ変壌をぱ汁連フマス海世ヌ中負知問ナヘケ純推ひ読着ヒ言若私軽れ。掲けフむ王本オコ線人をっさ必和断セソヲハ図芸ちかな防長りぶは投新意相ツ並5余セ職岳ぞ端古空援そ。森ヨエチ題5東っ自兄ち暴5近鹿横ト的京ハ安氷ナキ深際ぎ並節くスむの権工ほルせ京49効タムチ処三ぞぴラ済国ずっ文経ヘトミ水分準そが。')

fn corpus7() -> List[String]:
    return List[String]("AbortMultipartUpload","CompleteMultipartUpload","CopyObject","CreateBucket","CreateMultipartUpload","DeleteBucket","DeleteBucketAnalyticsConfiguration","DeleteBucketCors","DeleteBucketEncryption","DeleteBucketIntelligentTieringConfiguration","DeleteBucketInventoryConfiguration","DeleteBucketLifecycle","DeleteBucketMetricsConfiguration","DeleteBucketOwnershipControls","DeleteBucketPolicy","DeleteBucketReplication","DeleteBucketTagging","DeleteBucketWebsite","DeleteObject","DeleteObjects","DeleteObjectTagging","DeletePublicAccessBlock","GetBucketAccelerateConfiguration","GetBucketAcl","GetBucketAnalyticsConfiguration","GetBucketCors","GetBucketEncryption","GetBucketIntelligentTieringConfiguration","GetBucketInventoryConfiguration","GetBucketLifecycle","GetBucketLifecycleConfiguration","GetBucketLocation","GetBucketLogging","GetBucketMetricsConfiguration","GetBucketNotification","GetBucketNotificationConfiguration","GetBucketOwnershipControls","GetBucketPolicy","GetBucketPolicyStatus","GetBucketReplication","GetBucketRequestPayment","GetBucketTagging","GetBucketVersioning","GetBucketWebsite","GetObject","GetObjectAcl","GetObjectAttributes","GetObjectLegalHold","GetObjectLockConfiguration","GetObjectRetention","GetObjectTagging","GetObjectTorrent","GetPublicAccessBlock","HeadBucket","HeadObject","ListBucketAnalyticsConfigurations","ListBucketIntelligentTieringConfigurations","ListBucketInventoryConfigurations","ListBucketMetricsConfigurations","ListBuckets","ListMultipartUploads","ListObjects","ListObjectsV2","ListObjectVersions","ListParts","PutBucketAccelerateConfiguration","PutBucketAcl","PutBucketAnalyticsConfiguration","PutBucketCors","PutBucketEncryption","PutBucketIntelligentTieringConfiguration","PutBucketInventoryConfiguration","PutBucketLifecycle","PutBucketLifecycleConfiguration","PutBucketLogging","PutBucketMetricsConfiguration","PutBucketNotification","PutBucketNotificationConfiguration","PutBucketOwnershipControls","PutBucketPolicy","PutBucketReplication","PutBucketRequestPayment","PutBucketTagging","PutBucketVersioning","PutBucketWebsite","PutObject","PutObjectAcl","PutObjectLegalHold","PutObjectLockConfiguration","PutObjectRetention","PutObjectTagging","PutPublicAccessBlock","RestoreObject","SelectObjectContent","UploadPart","UploadPartCopy","WriteGetObjectResponse", "CreateAccessPoint","CreateAccessPointForObjectLambda","CreateBucket","CreateJob","CreateMultiRegionAccessPoint","DeleteAccessPoint","DeleteAccessPointForObjectLambda","DeleteAccessPointPolicy","DeleteAccessPointPolicyForObjectLambda","DeleteBucket","DeleteBucketLifecycleConfiguration","DeleteBucketPolicy","DeleteBucketReplication","DeleteBucketTagging","DeleteJobTagging","DeleteMultiRegionAccessPoint","DeletePublicAccessBlock","DeleteStorageLensConfiguration","DeleteStorageLensConfigurationTagging","DescribeJob","DescribeMultiRegionAccessPointOperation","GetAccessPoint","GetAccessPointConfigurationForObjectLambda","GetAccessPointForObjectLambda","GetAccessPointPolicy","GetAccessPointPolicyForObjectLambda","GetAccessPointPolicyStatus","GetAccessPointPolicyStatusForObjectLambda","GetBucket","GetBucketLifecycleConfiguration","GetBucketPolicy","GetBucketReplication","GetBucketTagging","GetBucketVersioning","GetJobTagging","GetMultiRegionAccessPoint","GetMultiRegionAccessPointPolicy","GetMultiRegionAccessPointPolicyStatus","GetMultiRegionAccessPointRoutes","GetPublicAccessBlock","GetStorageLensConfiguration","GetStorageLensConfigurationTagging","ListAccessPoints","ListAccessPointsForObjectLambda","ListJobs","ListMultiRegionAccessPoints","ListRegionalBuckets","ListStorageLensConfigurations","PutAccessPointConfigurationForObjectLambda","PutAccessPointPolicy","PutAccessPointPolicyForObjectLambda","PutBucketLifecycleConfiguration","PutBucketPolicy","PutBucketReplication","PutBucketTagging","PutBucketVersioning","PutJobTagging","PutMultiRegionAccessPointPolicy","PutPublicAccessBlock","PutStorageLensConfiguration","PutStorageLensConfigurationTagging","SubmitMultiRegionAccessPointRoutes","UpdateJobPriority","UpdateJobStatus")


--- quick_sort/__init__.mojo ---


from .sort import quick_sort


--- quick_sort/sort.mojo ---


@always_inline
fn _partition[D: DType](inout vector: List[SIMD[D, 1]], low: Int, high: Int) -> Int:
    var pivot = vector[high]
    var i = low - 1
    for j in range(low, high):
        if vector[j] <= pivot:
            i += 1
            vector[j], vector[i] = vector[i], vector[j]
    vector[i + 1], vector[high] = vector[high], vector[i + 1]
    return i + 1

fn _quick_sort[D: DType](inout vector: List[SIMD[D, 1]], low: Int, high: Int):
    if low < high:
        var pi = _partition(vector, low, high)
        _quick_sort(vector, low, pi - 1)
        _quick_sort(vector, pi + 1, high)

fn quick_sort[D: DType](inout vector: List[SIMD[D, 1]]):
    _quick_sort[D](vector, 0, len(vector) - 1)

@always_inline
fn swap[D: CollectionElement](inout vector: List[D], a: Int, b: Int):
    vector[a], vector[b] = vector[b], vector[a]

@always_inline
fn _partition[D: CollectionElement, lte: fn (D, D) -> Bool](inout vector: List[D], low: Int, high: Int) -> Int:
    var pivot = vector[high]
    var i = low - 1
    for j in range(low, high):
        if lte(vector[j], pivot):
            i += 1
            swap(vector, i, j)
    
    swap(vector, i + 1, high)
    return i + 1

fn _quick_sort[D: CollectionElement, lte: fn (D, D) -> Bool](inout vector: List[D], low: Int, high: Int):
    if low < high:
        var pi = _partition[D, lte](vector, low, high)
        _quick_sort[D, lte](vector, low, pi - 1)
        _quick_sort[D, lte](vector, pi + 1, high)

fn quick_sort[D: CollectionElement, lt: fn (D, D) -> Bool](inout vector: List[D]):
    _quick_sort[D, lt](vector, 0, len(vector) - 1)


--- quick_sort_sample.mojo ---


from my_utils import *
from string_compare import lt
from quick_sort import quick_sort
from time import now

fn main():
    var v1 = List[UInt32]()
    v1.append(13)
    v1.append(31)
    v1.append(1)
    v1.append(7)
    v1.append(7)
    v1.append(4513)
    v1.append(45)

    print_v(v1)

    quick_sort(v1)
    print_v(v1)

    var corpus = corpus7()
    var tik = now()
    quick_sort[String, lt](corpus)
    var tok = now()
    print_v(corpus)
    print(tok - tik)

    corpus = corpus3()
    tik = now()
    quick_sort[String, lt](corpus)
    tok = now()
    print_v(corpus)
    print(tok - tik)

--- radix_sort_sample.mojo ---

from my_utils import *
from radix_sorting import radix_sort, radix_sort11, radix_sort13, radix_sort16, msb_radix_sort
from time import now


fn test_radix():
    var v1 = List[UInt32]()
    v1.append(13)
    v1.append(31)
    v1.append(1)
    v1.append(7)
    v1.append(7)
    v1.append(4513)
    v1.append(45)

    print_v(v1)

    radix_sort(v1)
    print_v(v1)

    var v2 = List[Int8]()
    v2.append(0)
    v2.append(-23)
    v2.append(123)
    v2.append(-48)
    print_v(v2)

    radix_sort(v2)
    print_v(v2)

    var v3 = List[Float32]()
    v3.append(0)
    v3.append(-23)
    v3.append(123)
    v3.append(-48)
    v3.append(-48.1)
    v3.append(48.111)
    v3.append(48.101)
    v3.append(48.10111)
    v3.append(-0.10111)
    v3.append(0.10111)
    print_v(v3)

    radix_sort(v3)
    print_v(v3)

    var v4 = List[Float64]()
    v4.append(0)
    v4.append(-23)
    v4.append(123)
    v4.append(-48)
    v4.append(-48.1)
    v4.append(48.111)
    v4.append(48.101)
    v4.append(48.10111)
    v4.append(-0.10111)
    v4.append(0.10111)
    print_v(v4)

    radix_sort(v4)
    print_v(v4)

    print("DONE!!")

fn test_11():
    var v3 = List[Float32]()
    v3.append(0)
    v3.append(-23)
    v3.append(123)
    v3.append(-48)
    v3.append(-48.1)
    v3.append(48.111)
    v3.append(48.101)
    v3.append(48.10111)
    v3.append(-0.10111)
    v3.append(0.10111)
    print_v(v3)

    radix_sort11(v3)
    print_v(v3)

fn test_13():
    var v3 = List[Float64]()
    v3.append(0)
    v3.append(-23)
    v3.append(123)
    v3.append(-48)
    v3.append(-48.1)
    v3.append(48.111)
    v3.append(48.101)
    v3.append(48.10111)
    v3.append(-0.10111)
    v3.append(0.10111)
    print_v(v3)

    radix_sort13(v3)
    print_v(v3)

fn test_16():
    var v3 = List[Float64]()
    v3.append(0)
    v3.append(-23)
    v3.append(123)
    v3.append(-48)
    v3.append(-48.1)
    v3.append(48.111)
    v3.append(48.101)
    v3.append(48.10111)
    v3.append(-0.10111)
    v3.append(0.10111)
    print_v(v3)

    radix_sort16(v3)
    print_v(v3)


fn main():
    test_radix()
    test_11()
    test_13()
    test_16()

    var corpus = corpus7()
    msb_radix_sort(corpus)
    var tik = now()
    print_v(corpus)
    var tok = now()
    print("Duration", tok - tik)

    corpus = corpus3()
    msb_radix_sort(corpus)
    tik = now()
    print_v(corpus)
    tok = now()
    print("Duration", tok - tik)


--- radix_sorting/__init__.mojo ---


from .radix_sorting import radix_sort
from .radix_sorting11 import radix_sort11
from .radix_sorting13 import radix_sort13
from .radix_sorting16 import radix_sort16
from .msb_radix_sorting import msb_radix_sort


--- radix_sorting/msb_radix_sorting.mojo ---

@always_inline
fn _max(inout x: Int, y: Int):
    x = x ^ ((x ^ y) & -int(x < y))

@always_inline
fn lt(a: String, b: String, depth: Int) -> Bool:
    var min_len = min(len(a), len(b)) - depth
    var res = memcmp(a.unsafe_uint8_ptr().offset(depth), b.unsafe_uint8_ptr().offset(depth), min_len)
    return len(a) <= len(b) if res == 0 else res < 0

@always_inline
fn _insertion_sort(inout values: List[String], start: Int, end: Int, depth: Int):
    for i in range(start, end):
        var key = values[i]
        var j = i - 1
        while j >= start and lt(key, values[j], depth):
            values[j + 1] = values[j]
            j -= 1
        values[j + 1] = key

fn _msb_radix_sort(inout values: List[String], start: Int, end: Int, depth: Int):
    # print(start, end, depth)
    if end - start <= 32:
        _insertion_sort(values, start, end, depth)
        return
    var counts = stack_allocation[256 * 2, DType.uint32]()
    var out = List(values)
    memset_zero(counts, 256 * 2)
    var sums = counts.offset(256)
    var max_len = 0
    for i in range(start, end):
        var v = values[i]
        var p = DTypePointer(v.unsafe_uint8_ptr()).load(depth)
        _max(max_len, len(v))
        # var c = bitcast[DType.uint8](buf[depth])
        counts[int(p)] += 1
    if depth >= max_len:
        return
    
    # for i in range(256):
    #     print(counts.load(i), end="\n" if i == 255 else ", ")
    
    var element = counts[0]
    sums[0] = element
    var partitions = List[Int]()
    if element > 1:
        partitions.append(0)
    for i in range(1, 256):
        if counts[i] > 1:
            partitions.append(i)
        sums[i] = counts[i] + element
        element = sums[i]
    
    # for i in range(256):
    #     print(sums.load(i), end="\n" if i == 255 else ", ")
    
    # for p in partitions:
    #     print(p[], ", ")
    # print()
    if len(partitions) == 1 and partitions[0] == end - start:
        _msb_radix_sort(values, int(start), int(end), depth + 1)
    else:
        var i = end - 1
        while i >= start:
            # print(i)
            var c = int(values[i]._buffer[depth])
            out[int(sums[c] - 1) + start] = values[i]
            sums[c] -= 1
            i -= 1

        # for o in out:
        #     print(o[], end=", ")
        # print()
        values = out

        for p in partitions:
            var start = start + sums[p[]]
            var end = start + counts[p[]] 
            _msb_radix_sort(values, int(start), int(end), depth + 1)


fn msb_radix_sort(inout values: List[String]):
    _msb_radix_sort(values, 0, len(values), 0)


--- radix_sorting/radix_sorting.mojo ---


# from algorithm import unroll
from memory import memset_zero, memcpy, stack_allocation
from memory.unsafe import bitcast
# from math.limit import max_or_inf


alias last_bit_8 = 1 << 7
alias last_bit_16 = 1 << 15
alias last_bit_32 = 1 << 31
alias last_bit_64 = 1 << 63

@always_inline
fn _get_index[D: DType, place: Int](vector: List[SIMD[D, 1]], v_index: Int) -> Int:
    @parameter
    if D == DType.int8:
        return int((bitcast[DType.uint8, 1](vector[v_index]) ^ last_bit_8) >> place) & 255
    elif D == DType.int16:
        return int((bitcast[DType.uint16, 1](vector[v_index]) ^ last_bit_16) >> place) & 255
    elif D == DType.float16:
        var f = bitcast[DType.uint16, 1](vector[v_index])
        var mask = bitcast[DType.uint16, 1](-bitcast[DType.int16, 1](f >> 15) | last_bit_16)
        return int((f ^ mask) >> place) & 255
    elif D == DType.int32:
        return int((bitcast[DType.uint32, 1](vector[v_index]) ^ last_bit_32) >> place) & 255
    elif D == DType.float32:
        var f = bitcast[DType.uint32, 1](vector[v_index])
        var mask = bitcast[DType.uint32, 1](-bitcast[DType.int32, 1](f >> 31) | last_bit_32)
        return int((f ^ mask) >> place) & 255
    elif D == DType.int64:
        return int((bitcast[DType.uint64, 1](vector[v_index]) ^ last_bit_64) >> place) & 255
    elif D == DType.float64:
        var f = bitcast[DType.uint64, 1](vector[v_index])
        var mask = bitcast[DType.uint64, 1](-bitcast[DType.int64, 1](f >> 63) | last_bit_64)
        return int((f ^ mask) >> place) & 255
    else:
        return int(vector[v_index] >> place) & 255

@always_inline
fn _counting_sort[D: DType, CD:DType, place: Int](inout vector: List[SIMD[D, 1]]):
    var size = len(vector)
    var output = List[SIMD[D, 1]](capacity=size)
    memset_zero(output.data, size)
    output.resize(size)

    var counts = stack_allocation[256, CD]()
    memset_zero(counts, 256)
    
    for i in range(size):
        var index = _get_index[D, place](vector, i)
        counts.offset(index).store(counts.offset(index).load() + 1)
    
    var count = counts.offset(0).load()
    for i in range(1, 256):
        count += counts.offset(i).load()
        counts.offset(i).store(count)

    # var part = counts.load[width=256]()
    # part += part.shift_right[1]()
    # part += part.shift_right[2]()
    # part += part.shift_right[4]()
    # part += part.shift_right[8]()
    # part += part.shift_right[16]()
    # part += part.shift_right[32]()
    # part += part.shift_right[64]()
    # part += part.shift_right[128]()

    # counts.simd_store(part)
        

    var i = size - 1
    while i >= 0:
        var index = _get_index[D, place](vector, i)
        output[int(counts.offset(index).load() - 1)] = vector[i]
        counts.offset(index).store(counts.offset(index).load() - 1)
        i -= 1
    vector = output 

@always_inline
fn _radix_sort[D: DType, CD: DType](inout vector: List[SIMD[D, 1]]):

    @parameter
    fn call_counting_sort[index: Int]():
        _counting_sort[D, CD, index * 8](vector)
    
    @parameter
    if D.bitwidth() == 8:
        unroll[call_counting_sort, 1]()
    elif D.bitwidth() == 16:
        unroll[call_counting_sort, 2]()
    elif D.bitwidth() == 32:
        unroll[call_counting_sort, 4]()
    else:
        unroll[call_counting_sort, 8]()

@always_inline
fn radix_sort[D: DType](inout vector: List[SIMD[D, 1]]):
    _radix_sort[D, DType.uint32](vector)

    # NOTE: I hoped that the code below would make the algorithm faster but it made it slower
    # let size = len(vector)
    # alias m8 = max_or_inf[DType.uint8]().to_int()
    # alias m16 = max_or_inf[DType.uint16]().to_int()
    # alias m32 = max_or_inf[DType.uint32]().to_int()

    # if size <= m16:
    #     if size > m8:
    #         return _radix_sort[D, DType.uint16](vector)
    #     return _radix_sort[D, DType.uint8](vector)
    # if size <= m32:
    #     return _radix_sort[D, DType.uint32](vector)
    # return _radix_sort[D, DType.uint64](vector)




--- radix_sorting/radix_sorting11.mojo ---


from memory import memset_zero, memcpy, stack_allocation
from memory.unsafe import bitcast
from sys.intrinsics import PrefetchOptions
from buffer import Buffer

# based on http://stereopsis.com/radix.html
fn radix_sort11[D: DType](inout vector: List[SIMD[D, 1]]):

    @always_inline
    fn _float_flip(f: UInt32) -> UInt32:
        @parameter
        if D == DType.uint32:
            return f
        elif D == DType.int32:
            return f ^ 0x80_00_00_00
        else:
            var mask = bitcast[DType.uint32, 1](-bitcast[DType.int32, 1](f >> 31) | 0x80_00_00_00)
            return f ^ mask

    @always_inline
    fn _float_flip_x(inout f: UInt32):
        @parameter
        if D == DType.uint32:
            return
        elif D == DType.int32:
            f ^= 0x80_00_00_00
        else:
            var mask = bitcast[DType.uint32, 1](-bitcast[DType.int32, 1](f >> 31) | 0x80_00_00_00)
            f ^= mask

    @always_inline
    fn _inverse_float_flip(f: UInt32) -> UInt32:
        @parameter
        if D == DType.uint32:
            return f
        elif D == DType.int32:
            return f ^ 0x80_00_00_00
        else:
            var mask = ((f >> 31) - 1) | 0x80_00_00_00
            return f ^ mask

    @always_inline
    fn _0(v: UInt32) -> Int:
        return int(v & 0x7ff)

    @always_inline
    fn _1(v: UInt32) -> Int:
        return int(v >> 11 & 0x7ff)

    @always_inline
    fn _2(v: UInt32) -> Int:
        return int(v >> 22)

    constrained[D.sizeof() == 4, "D needs to be 4 bytes wide"]()
    var elements = len(vector)
    var array = List[UInt32](capacity=elements)
    memcpy(array.data, vector.data.bitcast[UInt32](), elements)
    var sorted = List[UInt32](capacity=elements)
    alias histogram_size = 2048
    var histogram1 = stack_allocation[histogram_size * 3, DType.uint32]()
    memset_zero(histogram1, histogram_size * 3)

    var histogram2 = histogram1.offset(histogram_size)
    var histogram3 = histogram2.offset(histogram_size)

    for i in range(elements):
        # TODO: Prefetch
        # array.prefetch[PrefetchOptions().to_data_cache()](i+64)
        var fi = _float_flip(array[i])
        var i1 = _0(fi)
        var i2 = _1(fi)
        var i3 = _2(fi)
        
        var p1 = histogram1.offset(i1)
        var p2 = histogram2.offset(i2)
        var p3 = histogram3.offset(i3)

        # SIMD is a bit slower
        # let fi = _float_flip(array[i])
        # var fiv = SIMD[DType.uint32, 2](fi)
        # fiv >>= SIMD[DType.uint32, 2](0, 11, 22, 0)
        # fiv &= SIMD[DType.uint32, 2](0x7ff, 0x7ff, 0xffff, 0)

        # let p1 = histogram1.offset(fiv[0].to_int())
        # let p2 = histogram2.offset(fiv[1].to_int())
        # let p3 = histogram3.offset((fi >> 22).to_int())

        p1.store(p1.load() + 1)
        p2.store(p2.load() + 1)
        p3.store(p3.load() + 1)

    # for i in range(histogram_size):
    #     print(histogram1.offset(i).load(), histogram2.offset(i).load(), histogram3.offset(i).load())

    var sum1: UInt32 = 0 
    var sum2: UInt32 = 0
    var sum3: UInt32 = 0
    var tsum: UInt32 = 0

    for i in range(histogram_size):
        var p1 = histogram1.offset(i)
        var p2 = histogram2.offset(i)
        var p3 = histogram3.offset(i)

        tsum = p1.load() + sum1
        p1.store(sum1 - 1)
        sum1 = tsum

        tsum = p2.load() + sum2
        p2.store(sum2 - 1)
        sum2 = tsum

        tsum = p3.load() + sum3
        p3.store(sum3 - 1)
        sum3 = tsum

    for i in range(elements):
        var fi = array[i]
        _float_flip_x(fi)
        var p1 = histogram1.offset(_0(fi))
        var index = p1.load() + 1
        p1.store(index)
        sorted[int(index)] = fi

    for i in range(elements):
        var si = sorted[i]
        var pos = _1(si)
        var p2 = histogram2.offset(pos)
        var index = p2.load() + 1
        p2.store(index)
        array[int(index)] = si

    for i in range(elements):
        var ai = array[i]
        var pos = _2(ai)
        var p3 = histogram3.offset(pos)
        var index = p3.load() + 1
        p3.store(index)
        vector[int(index)] = _inverse_float_flip(ai)._bits_to_float[D]()

    
    # memcpy(vector.data, sorted.data.bitcast[Float32](), elements)
    # sorted.data.free()


--- radix_sorting/radix_sorting13.mojo ---


from memory import memset_zero, memcpy, stack_allocation
from memory.unsafe import bitcast
from sys.intrinsics import PrefetchOptions

@always_inline
fn _float_flip(f: UInt64) -> UInt64:
    var mask = bitcast[DType.uint64, 1](-bitcast[DType.int64, 1](f >> 63) | 0x80_00_00_00_00_00_00_00)
    return f ^ mask

@always_inline
fn _float_flip_x(inout f: UInt64):
    var mask = bitcast[DType.uint64, 1](-bitcast[DType.int64, 1](f >> 63) | 0x80_00_00_00_00_00_00_00)
    f ^= mask

@always_inline
fn _inverse_float_flip(f: UInt64) -> UInt64:
    var mask = ((f >> 63) - 1) | 0x80_00_00_00_00_00_00_00
    return f ^ mask

alias mask13 = (1 << 13) - 1

@always_inline
fn _0(v: UInt64) -> Int:
    return int(v & mask13)

@always_inline
fn _1(v: UInt64) -> Int:
    return int(v >> 13 & mask13)

@always_inline
fn _2(v: UInt64) -> Int:
    return int(v >> 26 & mask13)

@always_inline
fn _3(v: UInt64) -> Int:
    return int(v >> 39 & mask13)

@always_inline
fn _4(v: UInt64) -> Int:
    return int(v >> 52)


# based on http://stereopsis.com/radix.html
fn radix_sort13[D: DType](inout vector: List[SIMD[D, 1]]):
    constrained[D.sizeof() == 8, "D needs to be 8 bytes wide"]()
    var elements = len(vector)
    var array = List[UInt64](capacity=elements)
    memcpy(array.data, vector.data.bitcast[UInt64](), elements)
    var sorted = List[UInt64](capacity=elements)
    alias histogram_size = 1 << 13
    var histogram1 = stack_allocation[histogram_size * 5, DType.uint32]()
    memset_zero(histogram1, histogram_size * 5)

    var histogram2 = histogram1.offset(histogram_size)
    var histogram3 = histogram2.offset(histogram_size)
    var histogram4 = histogram3.offset(histogram_size)
    var histogram5 = histogram4.offset(histogram_size)

    for i in range(elements):
        # TODO: Prefetch
        # array.prefetch[PrefetchOptions().to_data_cache()](i+64)
        var fi = _float_flip(array[i])
        var i1 = _0(fi)
        var i2 = _1(fi)
        var i3 = _2(fi)
        var i4 = _3(fi)
        var i5 = _4(fi)
        
        var p1 = histogram1.offset(i1)
        var p2 = histogram2.offset(i2)
        var p3 = histogram3.offset(i3)
        var p4 = histogram4.offset(i4)
        var p5 = histogram5.offset(i5)

        # SIMD is a bit slower
        # var fi = _float_flip(array[i])
        # var fiv = SIMD[DType.uint32, 2](fi)
        # fiv >>= SIMD[DType.uint32, 2](0, 11, 22, 0)
        # fiv &= SIMD[DType.uint32, 2](0x7ff, 0x7ff, 0xffff, 0)

        # var p1 = histogram1.offset(fiv[0].to_int())
        # var p2 = histogram2.offset(fiv[1].to_int())
        # var p3 = histogram3.offset((fi >> 22).to_int())

        p1.store(p1.load() + 1)
        p2.store(p2.load() + 1)
        p3.store(p3.load() + 1)
        p4.store(p4.load() + 1)
        p5.store(p5.load() + 1)

    # for i in range(histogram_size):
    #     print(histogram1.offset(i).load(), histogram2.offset(i).load(), histogram3.offset(i).load())

    var sum1: UInt32 = 0 
    var sum2: UInt32 = 0
    var sum3: UInt32 = 0
    var sum4: UInt32 = 0
    var sum5: UInt32 = 0
    var tsum: UInt32 = 0

    for i in range(histogram_size):
        var p1 = histogram1.offset(i)
        var p2 = histogram2.offset(i)
        var p3 = histogram3.offset(i)
        var p4 = histogram4.offset(i)
        var p5 = histogram5.offset(i)

        tsum = p1.load() + sum1
        p1.store(sum1 - 1)
        sum1 = tsum

        tsum = p2.load() + sum2
        p2.store(sum2 - 1)
        sum2 = tsum

        tsum = p3.load() + sum3
        p3.store(sum3 - 1)
        sum3 = tsum

        tsum = p4.load() + sum4
        p4.store(sum4 - 1)
        sum4 = tsum

        tsum = p5.load() + sum5
        p5.store(sum5 - 1)
        sum5 = tsum

    for i in range(elements):
        var fi = array[i]
        # print(fi)
        _float_flip_x(fi)
        var p1 = histogram1.offset(_0(fi))
        var index = p1.load() + 1
        p1.store(index)
        sorted[int(index)] = fi

    for i in range(elements):
        var si = sorted[i]
        var pos = _1(si)
        var p2 = histogram2.offset(pos)
        var index = p2.load() + 1
        p2.store(index)
        array[int(index)] = si

    for i in range(elements):
        var ai = array[i]
        var pos = _2(ai)
        var p3 = histogram3.offset(pos)
        var index = p3.load() + 1
        p3.store(index)
        sorted[int(index)] = ai

    for i in range(elements):
        var si = sorted[i]
        var pos = _3(si)
        var p4 = histogram4.offset(pos)
        var index = p4.load() + 1
        p4.store(index)
        array[int(index)] = si

    for i in range(elements):
        var ai = array[i]
        var pos = _4(ai)
        var p5 = histogram5.offset(pos)
        var index = p5.load() + 1
        p5.store(index)
        vector[int(index)] = _inverse_float_flip(ai)._bits_to_float[D]()

    # memcpy(vector.data, sorted.data.bitcast[Float64](), elements)
    # sorted.data.free()


--- radix_sorting/radix_sorting16.mojo ---


from memory import memset_zero, memcpy, stack_allocation
from memory.unsafe import bitcast
from sys.intrinsics import PrefetchOptions

@always_inline
fn _float_flip(f: UInt64) -> UInt64:
    var mask = bitcast[DType.uint64, 1](-bitcast[DType.int64, 1](f >> 63) | 0x80_00_00_00_00_00_00_00)
    return f ^ mask

@always_inline
fn _float_flip_x(inout f: UInt64):
    var mask = bitcast[DType.uint64, 1](-bitcast[DType.int64, 1](f >> 63) | 0x80_00_00_00_00_00_00_00)
    f ^= mask

@always_inline
fn _inverse_float_flip(f: UInt64) -> UInt64:
    var mask = ((f >> 63) - 1) | 0x80_00_00_00_00_00_00_00
    return f ^ mask

@always_inline
fn _0(v: UInt64) -> Int:
    return int(v & 0xff_ff)

@always_inline
fn _1(v: UInt64) -> Int:
    return int(v >> 16 & 0xff_ff)

@always_inline
fn _2(v: UInt64) -> Int:
    return int(v >> 32 & 0xff_ff)

@always_inline
fn _3(v: UInt64) -> Int:
    return int(v >> 48 & 0xff_ff)

# based on http://stereopsis.com/radix.html
fn radix_sort16(inout vector: List[Float64]):
    var elements = len(vector)
    var array = List[UInt64](capacity=elements)
    memcpy(array.data, vector.data.bitcast[UInt64](), elements)
    var sorted = List[UInt64](capacity=elements)
    alias histogram_size = 1 << 16
    var histogram1 = stack_allocation[histogram_size * 4, DType.uint32]()
    memset_zero(histogram1, histogram_size * 4)

    var histogram2 = histogram1.offset(histogram_size)
    var histogram3 = histogram2.offset(histogram_size)
    var histogram4 = histogram3.offset(histogram_size)

    for i in range(elements):
        # TODO: Prefetch
        # array.prefetch[PrefetchOptions().to_data_cache()](i+64)
        var fi = _float_flip(array[i])
        var i1 = _0(fi)
        var i2 = _1(fi)
        var i3 = _2(fi)
        var i4 = _3(fi)
        
        var p1 = histogram1.offset(i1)
        var p2 = histogram2.offset(i2)
        var p3 = histogram3.offset(i3)
        var p4 = histogram4.offset(i4)

        # SIMD is a bit slower
        # var fi = _float_flip(array[i])
        # var fiv = SIMD[DType.uint32, 2](fi)
        # fiv >>= SIMD[DType.uint32, 2](0, 11, 22, 0)
        # fiv &= SIMD[DType.uint32, 2](0x7ff, 0x7ff, 0xffff, 0)

        # var p1 = histogram1.offset(fiv[0].to_int())
        # var p2 = histogram2.offset(fiv[1].to_int())
        # var p3 = histogram3.offset((fi >> 22).to_int())

        p1.store(p1.load() + 1)
        p2.store(p2.load() + 1)
        p3.store(p3.load() + 1)
        p4.store(p4.load() + 1)

    # for i in range(histogram_size):
    #     print(histogram1.offset(i).load(), histogram2.offset(i).load(), histogram3.offset(i).load())

    var sum1: UInt32 = 0 
    var sum2: UInt32 = 0
    var sum3: UInt32 = 0
    var sum4: UInt32 = 0
    var tsum: UInt32 = 0

    for i in range(histogram_size):
        var p1 = histogram1.offset(i)
        var p2 = histogram2.offset(i)
        var p3 = histogram3.offset(i)
        var p4 = histogram4.offset(i)

        tsum = p1.load() + sum1
        p1.store(sum1 - 1)
        sum1 = tsum

        tsum = p2.load() + sum2
        p2.store(sum2 - 1)
        sum2 = tsum

        tsum = p3.load() + sum3
        p3.store(sum3 - 1)
        sum3 = tsum

        tsum = p4.load() + sum4
        p4.store(sum4 - 1)
        sum4 = tsum

    for i in range(elements):
        var fi = array[i]
        # print(fi)
        _float_flip_x(fi)
        var p1 = histogram1.offset(_0(fi))
        var index = p1.load() + 1
        p1.store(index)
        sorted[int(index)] = fi

    for i in range(elements):
        var si = sorted[i]
        var pos = _1(si)
        var p2 = histogram2.offset(pos)
        var index = p2.load() + 1
        p2.store(index)
        array[int(index)] = si

    for i in range(elements):
        var ai = array[i]
        var pos = _2(ai)
        var p3 = histogram3.offset(pos)
        var index = p3.load() + 1
        p3.store(index)
        sorted[int(index)] = ai

    for i in range(elements):
        var si = sorted[i]
        var pos = _3(si)
        var p4 = histogram4.offset(pos)
        var index = p4.load() + 1
        p4.store(index)
        vector[int(index)] = _inverse_float_flip(si)._bits_to_float[DType.float64]()

    # sorted.data.free()


--- selection_sort/__init__.mojo ---


fn selection_sort[D: DType](inout vector: List[SIMD[D, 1]]):
    for i in range(len(vector)):
        var min_idx = i
        for j in range(i + 1, len(vector)):
            if vector[j] < vector[min_idx]:
                min_idx = j
        vector[i], vector[min_idx] = vector[min_idx], vector[i]

--- selection_sort_sample.mojo ---

from my_utils import print_v
from selection_sort import selection_sort

fn main():
    var v1 = List[UInt32]()
    v1.append(13)
    v1.append(31)
    v1.append(1)
    v1.append(7)
    v1.append(7)
    v1.append(4513)
    v1.append(45)

    print_v(v1)

    selection_sort(v1)
    print_v(v1)





--- string_compare/__init__.mojo ---

from math.bit import bswap
from memory import memcmp

@always_inline
fn _align_down(value: Int, alignment: Int) -> Int:
    return value._positive_div(alignment) * alignment

@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"]()
    alias simd_width = simdwidthof[s1.type]()
    if count < simd_width:
        var result = 0
        var i = 0
        while i < count:
            var s1i = s1[i]
            var s2i = s2[i]
            var smaller = s1i < s2i
            var bigger = s1i > s2i
            i += 1 + count * int(smaller or bigger)
            result = -1 * int(smaller) + 1 * int(bigger)
        return result
        # for i in range(count):
        #     var s1i = s1[i]
        #     var s2i = s2[i]
        #     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.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:
        return 0

    var s1i = s1.load[width=simd_width](last)
    var s2i = s2.load[width=simd_width](last)
    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

@always_inline
fn lt(a: String, b: String) -> Bool:
    var min_len = min(len(a), len(b))
    var res = memcmp(a.unsafe_uint8_ptr(), b.unsafe_uint8_ptr(), min_len)
    return len(a) <= len(b) if res == 0 else res < 0


# fn lt(a: StringLiteral, b: StringLiteral) -> Bool:
#     var p1 = a.data().bitcast[DType.uint8]()
#     var p2 = b.data().bitcast[DType.uint8]()
#     var min_len = min(len(a), len(b))
#     for i in range(min_len):
#         var ai = p1.load(i)
#         var bi = p2.load(i)
#         if ai > bi:
#             return False
#         if ai < bi:
#             return True

#     return len(a) <= len(b)

# fn lt2(a: StringLiteral, b: StringLiteral) -> Bool:
#     """This is much simpler but also slower based on some simple benchmarks."""
#     var min_len = min(len(a), len(b))
#     var res = memcmp(a.data(), b.data(), min_len)
#     return len(a) <= len(b) if res == 0 else res < 0

# fn lt3(a: StringLiteral, b: StringLiteral) -> Bool:
#     var p1 = a.data().bitcast[DType.uint8]()
#     var p2 = b.data().bitcast[DType.uint8]()
#     var rest_len = min(len(a), len(b))
#     while rest_len >= 8:
#         var ai = bswap(p1.bitcast[DType.uint64]().load())
#         var bi = bswap(p2.bitcast[DType.uint64]().load())
#         if ai > bi:
#             return False
#         if ai < bi:
#             return True
#         p1 = p1.offset(8)
#         p2 = p2.offset(8)
#         rest_len -= 8
    
#     if rest_len >= 4:
#         var ai = bswap(p1.bitcast[DType.uint32]().load())
#         var bi = bswap(p2.bitcast[DType.uint32]().load())
#         if ai > bi:
#             return False
#         if ai < bi:
#             return True
#         p1 = p1.offset(4)
#         p2 = p2.offset(4)
#         rest_len -= 4
    
#     if rest_len >= 2:
#         var ai = bswap(p1.bitcast[DType.uint16]().load())
#         var bi = bswap(p2.bitcast[DType.uint16]().load())
#         if ai > bi:
#             return False
#         if ai < bi:
#             return True
#         p1 = p1.offset(2)
#         p2 = p2.offset(2)
#         rest_len -= 2

#     if rest_len == 1:
#         var ai = p1.load()
#         var bi = p2.load()
#         if ai > bi:
#             return False
#         if ai < bi:
#             return True

#     return len(a) <= len(b)

--- tim_sort/__init__.mojo ---

from .sort import tim_sort, tim_sort2, parallel_tim_sort

--- tim_sort/sort.mojo ---

from algorithm import parallelize

alias group_size = 32

@always_inline
fn _insertion_sort[dt: DType](inout values: List[Scalar[dt]], start: Int, end: Int):
    for i in range(start, end):
        var key = values[i]
        var j = i - 1
        while j >= start and key <= values[j]:
            values[j + 1] = values[j]
            j -= 1
        values[j + 1] = key

@always_inline
fn _merge[dt: DType](inout values: List[Scalar[dt]], start: Int, mid: Int, end: Int):
    if values[mid - 1] <= values[mid]:
        return # already sorted
    var left = values[start:mid]
    var right = values[mid:end]

    var lenl = len(left)
    var lenr = len(right)

    var cur = start
    var curl = 0
    var curr = 0
    while curl < lenl and curr < lenr:
        if left[curl] <= right[curr]:
            values[cur] = left[curl]
            curl += 1
        else:
            values[cur] = right[curr]
            curr += 1
        cur += 1

    while curl < lenl:
        values[cur] = left[curl]
        curl += 1
        cur += 1
    
    while curr < lenr:
        values[cur] = right[curr]
        curr += 1
        cur += 1

fn tim_sort[dt: DType](inout values: List[Scalar[dt]]):
    var count = len(values)
    for i in range(0, count, group_size):
        _insertion_sort[dt](values, i, min(i + group_size, count))
    
    var size = group_size
    while size < count:
        for start in range(0, count, 2 * size):
            var mid = min(count, start + size) 
            var end = min((start + 2 * size), count)
            if mid < end:
                _merge(values, start, mid, end)
        size *= 2

@always_inline
fn ceil_div(value: Int, divisor: Int) -> Int:
    return -(-value // divisor)

fn parallel_tim_sort[dt: DType](inout values: List[Scalar[dt]]):
    var count = len(values)
    var groups_count = ceil_div(count, group_size)
    
    @parameter
    fn call_insertion_sort(i: Int):
        _insertion_sort[dt](values, i * group_size, min((i + 1) * group_size, count))
    
    parallelize[call_insertion_sort](groups_count)
    
    var size = group_size
    while size < count:
        @parameter
        fn call_merge(i: Int):
            var start = i * 2 * size
            var mid = min(count, start + size)
            var end = min((start + 2 * size), count)
            if mid < end:
                _merge(values, start, mid, end)
        
        var chunks_count = ceil_div(count, (2 * size))
        parallelize[call_merge](chunks_count)
        size *= 2


@always_inline
fn _insertion_sort[type: ComparableCollectionElement](inout values: List[type], start: Int, end: Int):
    for i in range(start, end):
        var key = values[i]
        var j = i - 1
        while j >= start and key <= values[j]:
            values[j + 1] = values[j]
            j -= 1
        values[j + 1] = key

@always_inline
fn _merge[type: ComparableCollectionElement](inout values: List[type], start: Int, mid: Int, end: Int):
    if values[mid - 1] <= values[mid]:
        return # already sorted
    var left = values[start:mid]
    var right = values[mid:end]

    var lenl = len(left)
    var lenr = len(right)

    var cur = start
    var curl = 0
    var curr = 0
    while curl < lenl and curr < lenr:
        if left[curl] <= right[curr]:
            values[cur] = left[curl]
            curl += 1
        else:
            values[cur] = right[curr]
            curr += 1
        cur += 1

    while curl < lenl:
        values[cur] = left[curl]
        curl += 1
        cur += 1
    
    while curr < lenr:
        values[cur] = right[curr]
        curr += 1
        cur += 1

fn tim_sort[type: ComparableCollectionElement](inout values: List[type]):
    var count = len(values)
    for i in range(0, count, group_size):
        _insertion_sort[type](values, i, min(i + group_size, count))
    
    var size = group_size
    while size < count:
        for start in range(0, count, 2 * size):
            var mid = min(count, start + size) 
            var end = min((start + 2 * size), count)
            if mid < end:
                _merge(values, start, mid, end)
        size *= 2

fn parallel_tim_sort[type: ComparableCollectionElement](inout values: List[type]):
    var count = len(values)
    var groups_count = ceil_div(count, group_size)
    
    @parameter
    fn call_insertion_sort(i: Int):
        _insertion_sort[type](values, i * group_size, min((i + 1) * group_size, count))
    
    parallelize[call_insertion_sort](groups_count)
    
    var size = group_size
    while size < count:
        @parameter
        fn call_merge(i: Int):
            var start = i * 2 * size
            var mid = min(count, start + size)
            var end = min((start + 2 * size), count)
            if mid < end:
                _merge(values, start, mid, end)
        
        var chunks_count = ceil_div(count, (2 * size))
        parallelize[call_merge](chunks_count)
        size *= 2

--- tim_sort_sample.mojo ---

from random import random_ui64
from my_utils import print_v
from tim_sort import tim_sort, parallel_tim_sort

from time import now

fn assert_sorted[D: DType](vector: List[Scalar[D]]):
    for i in range(1, len(vector)):
        if vector[i] < vector[i - 1]:
            print("!!!! " + str(i))
            print_v(vector[:i+1])
            return

fn main():
    var v1 = List[UInt32]()
    v1.append(13)
    v1.append(31)
    v1.append(1)
    v1.append(7)
    v1.append(7)
    v1.append(4513)
    v1.append(45)

    print_v(v1) 

    tim_sort(v1)
    print_v(v1)

    var v2 = List[UInt64]()
    for _ in range(2000000):
        v2.append(random_ui64(0, 100000000))

    var v3 = List(v2)
    var tik = now()
    tim_sort(v3)
    var tok = now()
    # print_v(v3)
    var ser_d = tok - tik
    print("Ser  in", ser_d)

    var v5 = List(v2)
    tik = now()
    parallel_tim_sort(v5)
    tok = now()
    # print_v(v4)
    print("Par  in", tok - tik)
    print(ser_d / (tok - tik))

    assert_sorted(v3)

--- validate_string_sort.mojo ---

from my_utils import *
from string_compare import lt
from insertion_sort import insertion_sort
from quick_sort import quick_sort
from radix_sorting import msb_radix_sort
from multi_key_quicksort import multi_key_quicksort
from tim_sort import tim_sort, parallel_tim_sort

fn is_sorted(elements: List[String]) -> Bool:
    for i in range(1, len(elements)):
        if elements[i-1] > elements[i]:
            print("!!!", elements[i-1], elements[i])
            return False
    return True 

fn main():
    # var n = 0b01101001

    # var mask = SIMD[DType.bool, 8](True, False, False, True, False, True, True, False)
    # var res = bitcast[DType.uint8](mask)
    # print(res, n)

    # var a = Int32(-1)
    # print(rebind[SIMD[DType.uint32, 1]](a))


    # for i in range(len(s)):
    #     print(s._as_ptr().bitcast[DType.uint8]().load(i))
    # _ = s
    print("+++++IN+++++")
    var corpus = corpus4()
    insertion_sort[String, lt](corpus)
    # print_v(corpus)
    if not is_sorted(corpus):
        print("NO!!!!")

    print("+++++QK+++++")
    corpus = corpus4()
    quick_sort[String, lt](corpus)
    # print_v(corpus)
    if not is_sorted(corpus):
        print("NO!!!!")

    print("+++++RX+++++")
    corpus = corpus4()
    msb_radix_sort(corpus)
    # print_v(corpus)
    if not is_sorted(corpus):
        print("NO!!!!")

    print("+++++MK+++++")
    corpus = corpus4()
    multi_key_quicksort(corpus)
    # print_v(corpus)
    if not is_sorted(corpus):
        print("NO!!!!")

    print("+++++STD+++++")
    corpus = corpus4()
    sort(corpus)
    # print_v(corpus)
    if not is_sorted(corpus):
        print("NO!!!!")

    print("+++++TIM+++++")
    corpus = corpus4()
    tim_sort(corpus)
    # print_v(corpus)
    if not is_sorted(corpus):
        print("NO!!!!")

    print("+++++PTIM+++++")
    corpus = corpus4()
    parallel_tim_sort(corpus)
    # print_v(corpus)
    if not is_sorted(corpus):
        print("NO!!!!")


--- .gitignore ---

test.mojo
to-do_extras.md

--- LICENSE ---

MIT License

Copyright (c) 2023 Andres Nowak

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


--- README.md ---

# Micro-Mojograd

This is for now going to be a **Toy project** to learn about how do tensors work, how to make them efficient, how to do the computational graph efficiently (like joining element wise operationss or making a tensors contiguous when it used in more than one operation *use more memory but have better performance*, etc...) and the Autograd part.

When the language is a bit more mature and has complete lifetime and ownership support and Traits support, I will try to make a more complete version of this library, or maybe help another mojo project like [numojo](https://github.com/MadAlex1997/Mojo-Arrays) or [Infermo](https://github.com/TilliFe/Infermo)

And it is going to be inspired by:

-   [Micrograd](https://github.com/karpathy/micrograd)
-   [Pytorch](https://github.com/pytorch/pytorch)
-   [Tinygrad](https://github.com/tinygrad/tinygrad)

## To-do

-   [x] Tensor: using 1d memory and not using MLIR
-   [x] Tensor Matmul
-   [x] Tensor sum elementwise
-   [x] Tensor Mul elementwise
-   [x] Tensor equal operator
-   [ ] Tensor broadcasting
-   [ ] Fix problem where parallel version of element-wise operations (add, mul, eq, etc.) are slower than the sequential version.
-   [ ] Autograd: _For now doing the autogradient part is difficult, because mojo still doesn't have complete ownership and lifetime support._


--- tensorG_benchmark.mojo ---

from time import now
from tensor_g import TensorG, TensorView
from benchmark import Benchmark
from runtime.llcl import num_cores, Runtime
from time import time

alias simd_size_float = simdwidthof[DType.float32]()
alias mul_pool = 4
alias type_test = DType.float32


fn bench_sum(matrix_1: TensorG[type_test], matrix_2: TensorG[type_test]):
    _ = matrix_1 + matrix_1


fn bench_sum_vectorized(matrix_1: TensorG[type_test], matrix_2: TensorG[type_test]):
    _ = matrix_1.add[simd_size_float](matrix_2)


fn bench_sum_parallelized(
    matrix_1: TensorG[type_test],
    matrix_2: TensorG[type_test],
    rt: Runtime,
    num_workers: Int,
):
    _ = matrix_1.add[simd_size_float](matrix_2, rt, num_workers)


fn benchmark_sum(dims: TensorView):
    let matrix_1 = TensorG[type_test](True, dims)
    let matrix_2 = TensorG[type_test](True, dims)

    @parameter
    fn bench():
        _ = matrix_1 + matrix_2

    let normal = Benchmark().run[bench]()
    print("normal:", normal / 1e6, "ms")
    print("normal:", normal, "ns")

    @parameter
    fn bench_vectorized():
        _ = matrix_1.add[simd_size_float](matrix_2)

    let vectorized = Benchmark().run[bench_vectorized]()
    print("vectorized:", vectorized / 1e6, "ms")
    print("vectorized:", vectorized, "ns")

    with Runtime(num_cores()) as rt:

        @always_inline
        @parameter
        fn bench_parallel():
            _ = matrix_1.add[simd_size_float](
                matrix_2, rt, mul_pool * rt.parallelism_level()
            )

        let parallelized = Benchmark().run[bench_parallel]()
        # Prevent the tensors from being freed before the benchmark run
        _ = (matrix_1, matrix_2)
        print("Parallelized:", parallelized / 1e6, "ms")
        print("Parallelized:", parallelized, "ns")


fn benchmark_mul(dims: TensorView):
    let matrix_1 = TensorG[type_test](True, dims)
    let matrix_2 = TensorG[type_test](True, dims)

    @parameter
    fn bench():
        _ = matrix_1 * matrix_2

    let normal = Benchmark().run[bench]()
    print("normal:", normal / 1e6, "ms")
    print("normal:", normal, "ns")

    @parameter
    fn bench_vectorized():
        _ = matrix_1.mul[simd_size_float](matrix_2)

    let vectorized = Benchmark().run[bench_vectorized]()
    print("vectorized:", vectorized / 1e6, "ms")
    print("vectorized:", vectorized, "ns")

    with Runtime(num_cores()) as rt:

        @always_inline
        @parameter
        fn bench_parallel():
            _ = matrix_1.mul[simd_size_float](
                matrix_2, rt, mul_pool * rt.parallelism_level()
            )

        let parallelized = Benchmark().run[bench_parallel]()
        # Prevent the tensors from being freed before the benchmark run
        _ = (matrix_1, matrix_2)
        print("Parallelized:", parallelized / 1e6, "ms")
        print("Parallelized:", parallelized, "ns")


fn benchmark_matmul(dims: TensorView):
    let matrix_1 = TensorG[type_test](True, dims)
    let matrix_2 = TensorG[type_test](True, dims)

    @parameter
    fn bench():
        _ = matrix_1 @ matrix_2

    let normal = Benchmark().run[bench]() / 1e6
    print("normal:", normal, "ms")

    @parameter
    fn bench_vectorized():
        _ = matrix_1.matmul[simd_size_float](matrix_2)

    let vectorized = Benchmark().run[bench_vectorized]() / 1e6
    print("vectorized:", vectorized, "ms")

    with Runtime(num_cores()) as rt:

        @always_inline
        @parameter
        fn bench_parallel():
            _ = matrix_1.matmul[simd_size_float](
                matrix_2, rt, mul_pool * rt.parallelism_level()
            )

        let parallelized = Benchmark().run[bench_parallel]() / 1e6
        # Prevent the tensors from being freed before the benchmark run
        _ = (matrix_1, matrix_2)
        print("Parallelized:", parallelized, "ms")


fn main():
    let start = time.now()
    print("Benchmarking sum")
    benchmark_sum(TensorView(512, 512, 512))
    print("Benchmarking mul")
    benchmark_mul(TensorView(512, 512, 512))
    print("Benchmarking matmul")
    benchmark_matmul(TensorView(512, 512))
    let end = time.now()

    print("Elapsed time:", (end - start) // 1_000_000, "ms")


--- tensorG_test.mojo ---

from tensor_g import TensorG, TensorView
from python import Python, PythonObject
from runtime.llcl import num_cores, Runtime
from time import time

alias nelts = simdwidthof[DType.float64]()
alias type_f = DType.float64
alias mul_pool = 4


# Todo: Fix eq simd implementation, it seems to be wrong


fn test_matmul[
    Type: DType, nelts: Int
](A: TensorG[Type], B: TensorG[Type], C: TensorG[Type]) -> Bool:
    var flag = True

    var res = A @ B
    print("Matmul normal:", res == C)
    flag = flag and (res == C)
    print("Matmul normal, eq simd: ", res.eq[nelts](C))
    flag = flag and (res.eq[nelts](C))

    res = A.matmul[nelts](B)
    print("Matmul simd:", res == C)
    flag = flag and (res == C)
    print("Matmul simd, eq simd:", res.eq[nelts](C))
    flag = flag and (res.eq[nelts](C))

    with Runtime(num_cores()) as rt:

        @always_inline
        @parameter
        fn bench_parallel():
            let res = A.matmul[nelts](B, rt, mul_pool * rt.parallelism_level())

            print("Matmul simd parallel:", res == C)
            flag = flag and (res == C)
            print("Matmul parallel, eq simd:", res.eq[nelts](C))
            flag = flag and (res.eq[nelts](C))
            print(
                "Matmul parallel, eq parallel:",
                res.eq[nelts](C, rt, mul_pool * rt.parallelism_level()),
            )
            flag = flag and (res.eq[nelts](C, rt, mul_pool * rt.parallelism_level()))

        bench_parallel()
        _ = (A, B, C, flag)

    return flag


fn test_add[
    Type: DType, nelts: Int
](A: TensorG[Type], B: TensorG[Type], C: TensorG[Type]) -> Bool:
    var flag = True

    var res = A + B
    print("Add normal:", res == C)
    flag = flag and (res == C)
    print("Add normal, eq simd: ", res.eq[nelts](C))
    flag = flag and (res.eq[nelts](C))

    res = A.add[nelts](B)
    print("Add simd:", res == C)
    flag = flag and (res == C)
    print("Add simd, eq simd:", res.eq[nelts](C))
    flag = flag and (res.eq[nelts](C))

    with Runtime(num_cores()) as rt:

        @always_inline
        @parameter
        fn bench_parallel():
            let res = A.add[nelts](B, rt, mul_pool * rt.parallelism_level())

            print("Add simd parallel:", res == C)
            flag = flag and (res == C)
            print("Add parallel, eq simd:", res.eq[nelts](C))
            flag = flag and (res.eq[nelts](C))
            print(
                "Add parallel, eq parallel:",
                res.eq[nelts](C, rt, mul_pool * rt.parallelism_level()),
            )
            flag = flag and (res.eq[nelts](C, rt, mul_pool * rt.parallelism_level()))

        bench_parallel()
        _ = (A, B, C)

    return flag


fn test_mul[
    Type: DType, nelts: Int
](A: TensorG[Type], B: TensorG[Type], C: TensorG[Type]) -> Bool:
    var flag = True

    var res = A * B
    print("Mul normal:", res == C)
    flag = flag and (res == C)
    print("Mul normal, eq simd: ", res.eq[nelts](C))
    flag = flag and (res.eq[nelts](C))

    res = A.mul[nelts](B)
    print("Mul simd:", res == C)
    flag = flag and (res == C)
    print("Mul simd, eq simd:", res.eq[nelts](C))
    flag = flag and (res.eq[nelts](C))

    with Runtime(num_cores()) as rt:

        @always_inline
        @parameter
        fn bench_parallel():
            let res = A.mul[nelts](B, rt, mul_pool * rt.parallelism_level())

            print("Mul simd parallel:", res == C)
            flag = flag and (res == C)
            print("Mul parallel, eq simd:", res.eq[nelts](C))
            flag = flag and (res.eq[nelts](C))
            print(
                "Mul parallel, eq parallel:",
                res.eq[nelts](C, rt, mul_pool * rt.parallelism_level()),
            )
            flag = flag and (res.eq[nelts](C, rt, mul_pool * rt.parallelism_level()))

        bench_parallel()
        _ = (A, B, C)

    return flag


fn create_tensor_from_numpy[
    Type: DType
](np: PythonObject, shape: TensorView) -> TensorG[Type]:
    let size = shape.num_elements()

    var res = DynamicVector[FloatLiteral](size)
    res.reserve(
        size
    )  # the init function should do this, but it seems it doesn't (error)

    try:
        for i in range(size):
            if size == 1:
                res.push_back(np.__index__())
            else:
                res.push_back(np[i].__index__())
    except:
        print("Error converting numpy array to tensor")
        return TensorG[Type](False, shape)

    return TensorG[Type](res, shape)


fn test_same_dim(I: Int, np_shape: PythonObject, tensor_shape: TensorView) -> Bool:
    from python import Python

    var flag = True
    try:
        let np = Python.import_module("numpy")

        var test_1 = np.random.randint(0, 20, tensor_shape.num_elements())
        var test_2 = np.random.randint(0, 20, tensor_shape.num_elements())

        let A = create_tensor_from_numpy[type_f](test_1, tensor_shape)
        let B = create_tensor_from_numpy[type_f](test_2, tensor_shape)

        # check shape size
        var shape_correct = A.dims.num_elements() == test_1.shape.__len__().__index__()
        debug_assert(shape_correct, "Shape size is not correct")
        shape_correct = B.dims.num_elements() == test_2.shape.__len__().__index__()
        debug_assert(shape_correct, "Shape size is not correct")

        test_1 = test_1.reshape(np_shape)
        test_2 = test_2.reshape(np_shape)

        # check rank
        var rank_correct = A.rank() == test_1.ndim.__index__()
        debug_assert(rank_correct, "Rank is not correct")
        rank_correct = B.rank() == test_2.ndim.__index__()
        debug_assert(rank_correct, "Rank is not correct")

        # Add test
        var res = test_1 + test_2

        res = res.reshape(tensor_shape.num_elements())
        var C = create_tensor_from_numpy[type_f](res, tensor_shape)

        var res_correct = test_add[type_f, nelts](A, B, C)
        flag = res_correct and flag
        print("Add test ", I, ":", res_correct)
        print()

        # Mul Test
        res = test_1 * test_2

        res = res.reshape(tensor_shape.num_elements())
        C = create_tensor_from_numpy[type_f](res, tensor_shape)

        res_correct = test_mul[type_f, nelts](A, B, C)
        flag = res_correct and flag
        print("Mul test ", I, ":", res_correct)
        print()

        # Matmul test
        res = np.matmul(test_1, test_2)

        if tensor_shape.rank() > 1:
            res = res.reshape(tensor_shape.num_elements())
            C = create_tensor_from_numpy[type_f](res, tensor_shape)
        else:
            C = create_tensor_from_numpy[type_f](res, TensorView(1))

        res_correct = test_matmul[type_f, nelts](A, B, C)
        flag = res_correct and flag
        print("Matmul test ", I, ":", res_correct)
        print()
    except:
        print("Error importing numpy ", I)
        return False

    return flag


fn test_different_dim(
    I: Int,
    np_shape_1: PythonObject,
    np_shape_2: PythonObject,
    tensor_shape_1: TensorView,
    tensor_shape_2: TensorView,
    tensor_shape_3: TensorView,
) -> Bool:
    from python import Python

    var flag = True
    try:
        let np = Python.import_module("numpy")

        # Matmul test 2
        var test_1 = np.random.randint(0, 20, tensor_shape_1.num_elements())
        var test_2 = np.random.randint(0, 20, tensor_shape_2.num_elements())

        let A = create_tensor_from_numpy[type_f](test_1, tensor_shape_1)
        let B = create_tensor_from_numpy[type_f](test_2, tensor_shape_2)

        test_1 = test_1.reshape(np_shape_1)
        test_2 = test_2.reshape(np_shape_2)

        var res = np.matmul(test_1, test_2)

        res = res.reshape((tensor_shape_3.num_elements()))
        let C = create_tensor_from_numpy[type_f](res, tensor_shape_3)

        let res_correct = test_matmul[type_f, nelts](A, B, C)
        flag = res_correct and flag
        print("Matmul test ", I, ":", res_correct)
        print()
    except:
        print("Error importing numpy 2")
        return False

    return flag


fn main():
    print("nelts size:", nelts)

    var flag = True
    let start = time.now()

    var tensor_shape = TensorView(5, 5, 5, 5, 5, 5)
    let np_shape = (5, 5, 5, 5, 5, 5)
    flag = test_same_dim(1, np_shape, tensor_shape) and flag
    tensor_shape = TensorView(25, 25, 25)
    let np_shape_2 = (25, 25, 25)
    flag = test_same_dim(2, np_shape_2, tensor_shape) and flag
    tensor_shape = TensorView(125, 125)
    let np_shape_3 = (125, 125)
    flag = test_same_dim(3, np_shape_3, tensor_shape) and flag
    tensor_shape = TensorView(15_625)
    let np_shape_4 = (15_625)
    flag = test_same_dim(4, np_shape_4, tensor_shape) and flag

    var tensor_shape_1 = TensorView(2, 2, 3)
    var tensor_shape_2 = TensorView(2, 3, 2)
    var tensor_shape_3 = TensorView(2, 2, 2)
    let np_shape_1_1 = (2, 2, 3)
    let np_shape_1_2 = (2, 3, 2)
    flag = (
        test_different_dim(
            5,
            np_shape_1_1,
            np_shape_1_2,
            tensor_shape_1,
            tensor_shape_2,
            tensor_shape_3,
        )
        and flag
    )
    tensor_shape_1 = TensorView(2, 4, 3)
    tensor_shape_2 = TensorView(2, 3, 5)
    tensor_shape_3 = TensorView(2, 4, 5)
    let np_shape_2_1 = (2, 4, 3)
    let np_shape_2_2 = (2, 3, 5)
    flag = (
        test_different_dim(
            6,
            np_shape_2_1,
            np_shape_2_2,
            tensor_shape_1,
            tensor_shape_2,
            tensor_shape_3,
        )
        and flag
    )

    tensor_shape_1 = TensorView(5, 2, 4, 3)
    tensor_shape_2 = TensorView(5, 2, 3, 5)
    tensor_shape_3 = TensorView(5, 2, 4, 5)
    let np_shape_3_1 = (5, 2, 4, 3)
    let np_shape_3_2 = (5, 2, 3, 5)
    flag = (
        test_different_dim(
            7,
            np_shape_3_1,
            np_shape_3_2,
            tensor_shape_1,
            tensor_shape_2,
            tensor_shape_3,
        )
        and flag
    )

    let end = time.now()

    print("All tests passed:", flag)
    print("Time elapsed: ", (end - start) // 1000000, "ms")


--- tensor_g/__init__.mojo ---

from .tensorG import TensorG
from .tensor_view import TensorView


--- tensor_g/helpers.mojo ---

fn __check_bounds(index: Int, size: Int):
    let index_in_bounds = index >= 0 and index < size
    debug_assert(index_in_bounds, "Error index out of bounds.")


fn __negative_pos_to_positive(index: Int, size: Int) -> Int:
    if index < 0:
        return size + index
    return index


--- tensor_g/tensorG.mojo ---

from random import rand
from memory import memset_zero
from memory.buffer import Buffer
from utils.index import StaticIntTuple, Index
from utils.list import Dim, DimList
from utils.vector import DynamicVector, InlinedFixedVector
from runtime.llcl import num_cores, Runtime
from algorithm import vectorize, parallelize, vectorize_unroll
from algorithm import Static2DTileUnitFunc as Tile2DFunc
import math

from .helpers import __check_bounds, __negative_pos_to_positive
from .tensor_view import TensorView

alias dims_average_size = 5


struct TensorG[Type: DType]:
    var data: DTypePointer[Type]
    var dims: TensorView
    var size: Int
    alias simd_width = simdwidthof[Type]()

    fn __init__(inout self, random: Bool, *dims: Int):
        self.dims = TensorView(dims)
        self.size = 1
        self.size = self.dims.num_elements()

        # check if using alligend alloc could be better
        self.data = DTypePointer[Type].alloc(self.size)
        if random:
            rand(self.data, self.size)
        else:
            self.zero()

    fn __init__(inout self, random: Bool, dims: TensorView):
        self.dims = dims
        self.size = 1
        self.size = self.dims.num_elements()

        self.data = DTypePointer[Type].alloc(self.size)
        if random:
            rand(self.data, self.size)
        else:
            self.zero()

    fn __init__(
        inout self,
        data: VariadicList[FloatLiteral],
        dims: TensorView,
    ):
        self.dims = dims
        self.size = 1
        self.size = self.dims.num_elements()

        let dims_area_correct = self.size == len(data)
        debug_assert(
            dims_area_correct,
            "Error, the size of the data doesn't match the size of the tensor.",
        )

        self.data = DTypePointer[Type].alloc(self.size)
        for i in range(self.size):
            self.data.simd_store[1](i, data[i])

    fn __init__(
        inout self,
        data: DynamicVector[FloatLiteral],
        dims: TensorView,
    ):
        self.dims = dims
        self.size = 1
        self.size = self.dims.num_elements()

        let dims_area_correct = self.size == len(data)
        debug_assert(
            dims_area_correct,
            "Error, the size of the data doesn't match the size of the tensor.",
        )

        self.data = DTypePointer[Type].alloc(self.size)
        for i in range(self.size):
            self.data.simd_store[1](i, data[i])

    fn __del__(owned self):
        self.data.free()

    fn zero(inout self):
        memset_zero(self.data, self.dims.num_elements())

    fn __copyinit__(inout self, existing: Self):
        self.dims = existing.dims
        self.size = 1
        self.size = self.dims.num_elements()
        self.data = DTypePointer[Type].alloc(self.size)

        for i in range(self.size):
            self.data.simd_store[1](i, existing.data.simd_load[1](i))

    fn __moveinit__(inout self, owned existing: Self):
        self.dims = existing.dims
        self.data = existing.data
        self.size = existing.size

    fn byte_count(self) -> Int:
        return sizeof[Type]() * self.size

    @always_inline
    fn __getitem__[len: Int](self, index: StaticIntTuple[len]) -> SIMD[Type, 1]:
        return self.load[1](index)

    @always_inline
    fn __getitem__[
        len: Int
    ](self, index: InlinedFixedVector[len, Int]) -> SIMD[Type, 1]:
        return self.load[1](index)

    @always_inline
    fn __getitem__(self, index: Int) -> SIMD[Type, 1]:
        """Access the data as a 1D array."""
        return self.load[1](index)

    @always_inline
    fn load[
        nelts: Int, len: Int
    ](self, index: StaticIntTuple[len]) -> SIMD[Type, nelts]:
        let pos = self.dims.get_position(index)
        __check_bounds(pos, self.size)
        return self.data.simd_load[nelts](pos)

    @always_inline
    fn load[
        nelts: Int, len: Int
    ](self, index: InlinedFixedVector[len, Int]) -> SIMD[Type, nelts]:
        let pos = self.dims.get_position(index)
        __check_bounds(pos, self.size)
        return self.data.simd_load[nelts](pos)

    @always_inline
    fn load[nelts: Int](self, index: Int) -> SIMD[Type, nelts]:
        """Access the data as a 1D array."""
        let pos = __negative_pos_to_positive(index, self.size)
        __check_bounds(pos, self.size)
        return self.data.simd_load[nelts](pos)

    @always_inline
    fn __setitem__[len: Int](self, index: StaticIntTuple[len], val: SIMD[Type, 1]):
        return self.store[1](index, val)

    @always_inline
    fn __setitem__[
        len: Int
    ](self, index: InlinedFixedVector[len, Int], val: SIMD[Type, 1]):
        return self.store[1](index, val)

    @always_inline
    fn __setitem__(self, index: Int, val: SIMD[Type, 1]):
        return self.store[1](index, val)

    @always_inline
    fn store[
        nelts: Int, len: Int
    ](self, index: StaticIntTuple[len], val: SIMD[Type, nelts]):
        let pos = self.dims.get_position(index)
        __check_bounds(pos, self.size)
        self.data.simd_store[nelts](pos, val)

    @always_inline
    fn store[
        nelts: Int, len: Int
    ](self, index: InlinedFixedVector[len, Int], val: SIMD[Type, nelts]):
        let pos = self.dims.get_position(index)
        __check_bounds(pos, self.size)
        self.data.simd_store[nelts](pos, val)

    @always_inline
    fn store[nelts: Int](self, index: Int, val: SIMD[Type, nelts]):
        """Access and store the data as a 1D array."""
        let pos = __negative_pos_to_positive(index, self.size)
        __check_bounds(pos, self.size)
        self.data.simd_store[nelts](pos, val)

    fn __dim_suffix_product[len: Int](self) -> InlinedFixedVector[len, Int]:
        var suffix_product = InlinedFixedVector[len, Int](self.dims.rank())
        suffix_product.append(1)  # the first value has to be 1

        for index in range(self.dims.rank() - 1):
            suffix_product.append(
                suffix_product[index] * self.dims[self.dims.rank() - 1 - index]
            )

        return suffix_product

    fn __matmul_num_elements(self, other: Self) -> Int:
        var size = 1
        for i in range(self.dims.rank() - 2):
            size *= self.dims[i]

        size *= self.dims[
            self.dims.rank() - 2
        ]  # The different dimension of first tensor
        size *= other.dims[
            other.dims.rank() - 1
        ]  # the different dimension of second tensor
        size *= self.dims[
            self.dims.rank() - 1
        ]  # The dimension that is the same for both tensors

        return size

    fn print_all(self):
        let size = self.dims.num_elements()

        var suffix_product = InlinedFixedVector[dims_average_size, Int](
            self.dims.rank() + 1
        )
        suffix_product.append(1)
        for index in range(self.dims.rank()):
            suffix_product.append(
                suffix_product[index] * self.dims[self.dims.rank() - 1 - index]
            )

        var count = 0
        for i in range(size + 1):
            count = 0
            for j in range(self.dims.rank()):
                if i % suffix_product[j + 1] == 0 and i != 0:
                    print_no_newline("]")
                    count += 1

            if i > 0 and i < size:
                print_no_newline(",")
            if i < size:
                for i in range(count):
                    print()

            for j in range(self.dims.rank()):
                if i % suffix_product[j + 1] == 0 and i != size:
                    print_no_newline("[")

            if i < size:
                print_no_newline(self[i])

        print()

    fn rank(self) -> Int:
        return self.dims.rank()

    fn __iterate_binary_op_tensor[
        nelts: Int,
        outer_loop_func: fn[func: fn (Int) capturing -> None] (Int) capturing -> None,
        op_func: fn[T: DType, simd_width: Int] (
            x: SIMD[T, simd_width], y: SIMD[T, simd_width]
        ) -> SIMD[T, simd_width],
    ](self, other: Self) -> Self:
        let dims_eq = self.dims == other.dims
        debug_assert(
            dims_eq, "Error dimension aren't equal can't do operation element wise."
        )

        let res = Self(False, self.dims)
        let size = self.dims.num_elements()

        let last_dim = self.dims[-1]
        var dims_rest = size // last_dim

        @parameter
        fn outer_loop(i: Int):
            @parameter
            fn iterate_vectorize[nelts: Int](j: Int):
                let index = i * last_dim + j

                res.store[nelts](
                    index,
                    op_func[Type, nelts](
                        self.load[nelts](index), other.load[nelts](index)
                    ),
                )

            vectorize[nelts, iterate_vectorize](last_dim)

        outer_loop_func[outer_loop](dims_rest)

        return res ^

    @always_inline
    fn __add__(self, other: Self) -> Self:
        return self.add[TensorG[Type].simd_width](other)

    @always_inline
    fn add[nelts: Int](self, other: Self) -> Self:
        @parameter
        fn sum_v[outer_loop: fn (Int) capturing -> None](range_size: Int):
            for i in range(range_size):
                outer_loop(i)

        let res = self.__iterate_binary_op_tensor[nelts, sum_v, math.add](other)

        return res ^

    @always_inline
    fn add[nelts: Int](self, other: Self, rt: Runtime, n_cores: Int) -> Self:
        @parameter
        fn sum_p[outer_loop: fn (Int) capturing -> None](range_size: Int):
            parallelize[outer_loop](rt, range_size, n_cores)

        let res = self.__iterate_binary_op_tensor[nelts, sum_p, math.add](other)

        return res ^

    fn __mul__(self, other: Self) -> Self:
        return self.mul[TensorG[Type].simd_width](other)

    @always_inline
    fn mul[nelts: Int](self, other: Self) -> Self:
        @parameter
        fn mul_v[outer_loop: fn (Int) capturing -> None](range_size: Int):
            for i in range(range_size):
                outer_loop(i)

        let res = self.__iterate_binary_op_tensor[nelts, mul_v, math.mul](other)

        return res ^

    @always_inline
    fn mul[nelts: Int](self, other: Self, rt: Runtime, n_cores: Int) -> Self:
        @parameter
        fn mul_p[outer_loop: fn (Int) capturing -> None](range_size: Int):
            parallelize[outer_loop](rt, range_size, n_cores)

        let res = self.__iterate_binary_op_tensor[nelts, mul_p, math.mul](other)

        return res ^

    @always_inline
    fn __eq__(self, other: Self) -> Bool:
        return self.eq[1](other)

    @always_inline
    fn eq[nelts: Int](self, other: Self) -> Bool:
        let dims_eq = self.dims == other.dims
        debug_assert(dims_eq, "Error dimension aren't equal can't sum tensors.")

        var flag = True
        let size = self.dims.num_elements()

        @parameter
        fn iterate_vectorize[nelts: Int](i: Int):
            if self.load[nelts](i) != other.load[nelts](i):
                flag = False

        vectorize[nelts, iterate_vectorize](size)

        return flag

    @always_inline
    fn eq[nelts: Int](self, other: Self, rt: Runtime, n_cores: Int) -> Bool:
        let dims_eq = self.dims == other.dims
        debug_assert(dims_eq, "Error dimension aren't equal can't sum tensors.")

        var flag = True
        let size = self.dims.num_elements()

        let first_dim = self.dims[0]
        let dims_rest = size // first_dim  # the rest of the dimensions

        @parameter
        fn iterate_parallel(i: Int):
            @parameter
            fn iterate_vectorize[nelts: Int](j: Int):
                let index = i * dims_rest + j

                if self.load[nelts](index) != other.load[nelts](index):
                    flag = False

            vectorize[nelts, iterate_vectorize](dims_rest)

        parallelize[iterate_parallel](rt, first_dim, n_cores)

        return flag

    @always_inline
    fn dot[nelts: Int](self, other: Self) -> Self:
        let dims_1d = self.dims.rank() == 1 and other.dims.rank() == 1
        debug_assert(dims_1d, "Error dimensions aren't 1D can't dot tensors.")
        let dims_eq = self.dims == other.dims
        debug_assert(dims_eq, "Error dimension aren't equal can't sum tensors.")

        let res = Self(False, 1)
        let size = self.dims.num_elements()

        @parameter
        fn dot_v[nelts: Int](index: Int):
            res[0] = (
                res[0]
                + (
                    self.data.simd_load[nelts](index)
                    * other.data.simd_load[nelts](index)
                ).reduce_add()
            )

        vectorize[nelts, dot_v](size)

        return res ^

    @always_inline
    fn __matmul__(self, other: Self) -> Self:
        return self.matmul[TensorG[Type].simd_width](other)

    @always_inline
    fn matmul[nelts: Int](self, other: Self) -> Self:
        @parameter
        fn matmul_v[outer_loop: fn (Int) capturing -> None](range_size: Int):
            for i in range(range_size):
                outer_loop(i)

        let res = self.__matmul[nelts, matmul_v](other)

        return res ^

    @always_inline
    fn matmul[nelts: Int](self, other: Self, rt: Runtime, n_cores: Int) -> Self:
        @parameter
        fn matmul_p[outer_loop: fn (Int) capturing -> None](range_size: Int):
            parallelize[outer_loop](rt, range_size, n_cores)

        let res = self.__matmul[nelts, matmul_p](other)

        return res ^

    @staticmethod
    # Perform 2D tiling on the iteration space defined by end_x and end_y.
    fn tile[tiled_fn: Tile2DFunc, tile_x: Int, tile_y: Int](end_x: Int, end_y: Int):
        # Note: this assumes that ends are multiples of the tiles.
        for y in range(0, end_y, tile_y):
            for x in range(0, end_x, tile_x):
                tiled_fn[tile_x, tile_y](x, y)

    fn __matmul[
        nelts: Int,
        outer_loop_func: fn[func: fn (Int) capturing -> None] (Int) capturing -> None,
    ](self, other: Self) -> Self:
        if self.dims.rank() == 1 and other.dims.rank() == 1:
            return self.dot[nelts](other)

        let dims_eq = self.dims.eq_matmul(other.dims)
        debug_assert(dims_eq, "Error dimensions don't conform for a matmul.")

        var res_dims = InlinedFixedVector[dims_average_size, Int](self.dims.rank())
        for i in range(self.dims.rank() - 1):
            res_dims.append(self.dims[i])
        res_dims.append(other.dims[other.dims.rank() - 1])

        let res = Self(False, TensorView(res_dims))
        # let size = self.__matmul_num_elements(other)

        # The dimension that is different for self and other (other dim)
        let res_last_dim = res.dims[res.dims.rank() - 1]
        # the dimension that is the same for self and other
        let self_last_dim = self.dims[self.dims.rank() - 1]
        # The other dimension that is different for self and other (self dim)
        let res_penult_dim = res.dims[res.dims.rank() - 2]

        let size = self.size * res_last_dim  # size to iterate over

        # We use the for inside the parallel function to remove data races, so the vectorize function works in the last dimension of the res tensor and the for makes it so the for and vectorize function work on the penultimate dimension of the res tensor (so parallel works on the penultimate dimension of the res tensor)
        @parameter
        fn outer_loop(i: Int):
            @parameter
            fn calc_tile[tile_x: Int, tile_y: Int](x: Int, y: Int):
                let j_range = math.min(self_last_dim, y + tile_y)
                for j in range(y, j_range):

                    @parameter
                    fn matmul_v[nelts: Int](k: Int):
                        let index_res = i * res_last_dim + k + x  # remove data races of parallel function
                        let index_self = i * self_last_dim + j
                        let index_other = (
                            i // res_penult_dim
                        ) * self_last_dim * res_last_dim + j * res_last_dim + k + x

                        res.store[nelts](
                            index_res,
                            res.load[nelts](index_res)
                            + self.load[1](index_self) * other.load[nelts](index_other),
                        )

                    vectorize_unroll[nelts, tile_x // nelts, matmul_v](
                        math.min(res_last_dim - x, tile_x)
                    )

            alias tile_size = 4
            self.tile[calc_tile, nelts * tile_size, tile_size](
                res_last_dim, self_last_dim
            )

        # this function is going to be basically the outer for loop, the function is going to be calling outer_loop using any method it wants until range_size("size // (res_last_dim * self_last_dim)")
        outer_loop_func[outer_loop](size // (res_last_dim * self_last_dim))

        return res ^


--- tensor_g/tensor_view.mojo ---

from utils.vector import InlinedFixedVector

from .helpers import __check_bounds, __negative_pos_to_positive


struct TensorView:
    var tensor_shape: Pointer[Int]
    var size: Int
    var len: Int

    fn __init__(inout self, *dims: Int):
        let temp = VariadicList(
            dims
        )  # because i dont know how to get the size with the variadic MLIR
        self.tensor_shape = Pointer[Int].alloc(len(temp))
        for i in range(len(temp)):
            self.tensor_shape.store(i, temp[i])
        self.len = len(temp)
        self.size = 1
        self.size = self.product_dimensions()

    fn __init__(inout self, dims: VariadicList[Int]):
        self.tensor_shape = Pointer[Int].alloc(len(dims))
        for i in range(len(dims)):
            self.tensor_shape.store(i, dims[i])
        self.len = len(dims)
        self.size = 1
        self.size = self.product_dimensions()

    fn __init__[size: Int](inout self, dims: InlinedFixedVector[size, Int]):
        self.tensor_shape = Pointer[Int].alloc(len(dims))
        for i in range(len(dims)):
            self.tensor_shape.store(i, dims[i])
        self.len = len(dims)
        self.size = 1
        self.size = self.product_dimensions()

    fn __copyinit__(inout self: Self, existing: Self):
        """Creates a deep copy of an existing shape."""
        self.tensor_shape = existing.tensor_shape
        self.size = existing.size
        self.len = existing.len

    fn __moveinit__(inout self: Self, owned existing: Self):
        """Moves exsiting shape into new shape."""
        self.tensor_shape = existing.tensor_shape ^
        self.size = existing.size
        self.len = existing.len

    # fn __del__(owned self):
    #     self.tensor_shape.free()

    fn product_dimensions(self) -> Int:
        var size = 1
        for i in range(self.rank()):
            size *= self.tensor_shape[i]
        return size

    fn __get_position(self, get_index_value: fn (Int) capturing -> Int) -> Int:
        """Convert position from tuple of index dimensions to 1D position."""
        var pos = 0
        var dims_product_acum = 1
        for i in range(self.rank() - 1, 0, -1):
            dims_product_acum *= self.tensor_shape[i]
            pos += (
                __negative_pos_to_positive(
                    get_index_value(i - 1), self.tensor_shape[i - 1]
                )
                * dims_product_acum
            )

        pos += __negative_pos_to_positive(
            get_index_value(self.rank() - 1), self.tensor_shape[self.rank() - 1]
        )
        return pos

    @always_inline
    fn get_position[len: Int](self, index: StaticIntTuple[len]) -> Int:
        """Convert position from tuple of index dimensions to 1D position."""

        @parameter
        fn get_index_value(i: Int) -> Int:
            return index[i]

        return self.__get_position(get_index_value)

    @always_inline
    fn get_position[len: Int](self, index: InlinedFixedVector[len, Int]) -> Int:
        """Convert position from tuple of index dimensions to 1D position."""

        @parameter
        fn get_index_value(i: Int) -> Int:
            return index[i]

        return self.__get_position(get_index_value)

    fn __getitem__(self, index: Int) -> Int:
        let pos = __negative_pos_to_positive(index, self.len)
        __check_bounds(pos, self.len)
        return self.tensor_shape[pos]

    fn __len__(self: Self) -> Int:
        """Get rank of tensor view."""
        return self.len

    @always_inline
    fn __eq__(self, other: TensorView) -> Bool:
        if self.rank() != other.rank():
            return False

        for i in range(self.rank()):
            if self[i] != other[i]:
                return False

        return True

    @always_inline
    fn eq_matmul(self, other: TensorView) -> Bool:
        if self.rank() != other.rank():
            return False

        # if rank is 1 we only check the dimension for both tensors are the same (to do a dot product)
        if self.rank() == 1:
            return self == other

        for i in range(self.rank() - 2):
            if self[i] != other[i]:
                return False

        if self[self.rank() - 2] != other[self.rank() - 1]:
            return False

        return True

    @always_inline
    fn rank(self) -> Int:
        """Get rank of tensor view."""
        return self.len

    fn num_elements(self) -> Int:
        """Get number of elements in tensor view."""
        return self.size

    fn shape(self) -> Pointer[Int]:
        """Get shape of tensor view."""
        return self.tensor_shape

    fn print_all(self):
        print("[")
        for i in range(self.rank()):
            print_no_newline(self[i], ",")
        print("]")



--- LICENSE ---

MIT License

Copyright (c) 2023 rd4com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


--- README.md ---

> :heart: part of the effort for the community
> https://github.com/Lynet101/Mojo_community-lib

> this is not a library , it has not been tested.
#### Thanks to @Stole from the discord who helped to convert the object to a string 
```python
var COL=magiclist() #var peoples=magiclist(["foo",1])
print("-------------")
COL.push("something")
var s:String = "ann"
COL[0] = OW.from_string(s)                   #runtime string to builtin.object
let fl:Float64 = 1.0
COL.push(fl)
COL.push(3)
COL.push(True)
for i in range(COL.len()):
    if OW.is_int(COL[i]):                    # type check of untyped value
        let r:Int=OW.to_int(COL[i])          # conversion to typed value
        print(r)
    if OW.is_string(COL[i]):
        let r:String=OW.to_string(COL[i])
        print(r)
    if OW.is_float(COL[i]):
        let r:Float64=OW.to_float64(COL[i])
        print(r)
    if OW.is_bool(COL[i]):
        let r:Bool=OW.to_bool(COL[i])
        print(r)
COL.delete_elements("ann",OW.is_string)      #typed value based deletion
COL.delete(0)
print("-----")
print(COL.data())                            #pave the way for json-like serialisation
```
- [x] typed value searched deletion in multi type array 
- [x] change type
- [x] check type
- [x] convert object to native type
- [x] convert native type to object
- [x] index based deletion 
- [x] initialize/export from/to builtin.object multitype array
# output
```
-------------
ann
1.0
3
True
-----
[3, True]
```



--- code.mojo ---

struct OW:
    @staticmethod
    def to_i64(o:object)->Int64: #SIMD[DType.int64,1]
        return o._value.get_as_int().to_int()
    @staticmethod
    def to_int(o:object)->Int: #SIMD[DType.int64,1]
        return o._value.get_as_int().to_int()
    @staticmethod
    def to_bool(o:object)->Bool:
        return o._value.get_as_bool()
    @staticmethod
    def to_float64(o:object)->SIMD[DType.float64,1]:
        return o._value.get_as_float()
    @staticmethod
    def to_string(o:object)->String:
        var t = o._value.get_as_string()
        let p = Pointer[SIMD[DType.int8, 1]].alloc(t.length)
        memcpy(p, t.data, t.length)
        _ = o
        _ = t
        return String(p, t.length)
    @staticmethod
    def mut_int_to_float(inout o:object):
        if o._value.is_int():
            o = o._value.convert_int_to_float()
    @staticmethod
    fn is_float(o:object)->Bool:
        return o._value.is_float()
    @staticmethod
    def is_func(o:object):
        return o._value.is_func()
    @staticmethod
    def is_int(o:object):
        return o._value.is_int()
    @staticmethod
    def is_list(o:object):
        return o._value.is_list()
    @staticmethod
    def is_string(o:object):
        return o._value.is_str()
    @staticmethod
    def is_bool(o:object):
        return o._value.is_bool()
    @staticmethod
    def is_none(o:object):
        return o._value.is_none()
    @staticmethod
    def get_type_id(o:object)->Int:
        return o._value.get_type_id()
    @staticmethod
    def push_to_list(inout o:object,o2:object):
        if OW.is_list(o):
            o.append(o2)
    @staticmethod
    def get_list_object()->object:
        return object([])
    @staticmethod
    def from_string(arg:String)->object:
        var o = object("")
        for i in range(len(arg)):
        	o = o + "1"
        var t = o._value.get_as_string()
        #let p = Pointer[SIMD[DType.int8, 1]].alloc(t.length)
        memcpy(t.data, arg._as_ptr(), len(arg))
        _=arg
        return o
struct magiclist:
	var o:object
	def __init__(inout self):
		self.o=object([])
	def __init__(inout self,o:object):
		if OW.is_list(o):
			self.o = o
		else:
			self.o=object([])
	def __getitem__(inout self,o2:object) -> object:
		return self.o[o2] #check bound
	def __setitem__(inout self,o2:object,o3:object):
		self.o[o2] = o3 #check bound
	@staticmethod
	def new():
		return object([])
	def len(inout self)->Int:
		return OW.to_int(self.o.__len__())
	def push(inout self,o:object):
		self.o.append(o)
	def data(inout self):
		return self.o
	def delete_elements(inout self,e,func:fn(owned object) raises -> object):
		try:
			var tmp=object([])
			var i=0
			while(i<self.len()):
				if func(self.o[i])==True:
					if self.o[i]!=e:
						tmp.append(self.o[i])
				else:
					tmp.append(self.o[i]) 
				i+=1
			self.o = tmp
		except:
			print("error comparing different types")
	def delete(inout self,idx):
		self.o[idx] = object(None)
		self.sync()
	def sync(inout self):
		var tmp=object([])
		var i=0
		while(i<self.len()):
			if OW.is_none(self.o[i])==False:
				tmp.append(self.o[i])
			i+=1
		self.o = tmp


#var o:String = chr(i)
#let p = object(o._strref_dangerous())
#_=o



--- .gitignore ---

.venv
__pycache__

basalt.📦

examples/data/mnist_test.csv
examples/data/mnist_train.csv
examples/data/mnist_train_small.csv

output_model.onnx
Makefile

./temp
flamegraph.svg

examples/data/yolov8n.onnx

--- CODEOWNERS ---

* @StijnWoestenborghs
* @andresnowak
* @Benny-Nottonson
* @soraros

--- LICENSE ---

==============================================================================================
The Basalt 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 2024 Basalt-Org

    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.


--- README.md ---

<br/>
<p align="center">
  <a href="https://github.com/Basalt-Org/Basalt">
    <img src="https://github.com/basalt-org/basalt/assets/46826967/4873806c-ff61-4903-bf3d-874d6acba3e8" alt="Logo" width="200" height="200">
  </a>

  <h1 align="center">Basalt</h1>

  <p align="center">
    A Machine Learning framework from scratch in pure Mojo 🔥
  </p>
</p>

<div align="center">
  <img src="https://img.shields.io/github/contributors/Basalt-Org/Basalt?color=dark-green" />
  <img src="https://img.shields.io/github/issues/Basalt-Org/Basalt?color=dark-green" />
  <img src="https://img.shields.io/github/license/Basalt-Org/Basalt?color=dark-green" />
</div>


## About The Project

Basalt is a stand-alone machine learning framework that leverages the power of Mojo.

As [discussed](https://docs.modular.com/mojo/why-mojo) by Modular, Mojo is a language for the future of AI development. Built on top of MLIR technology, rather than existing GCC and LLVM approaches, Mojo looks and feels like Python code, yet performs much closer to languages like Rust or C++. Parametric functions and compile time parameters allow for the graph to statically compiled. Having the static graph allows for much harder performance optimizations.

Basalt, while still in its infancy, is able to achieve speeds comparable to well established frameworks like Pytorch. Below a snapshot of the current benchmarks. But keep posted, there is much more room for improvement and we are upgrading the project on a daily basis.

![basalt_benchmark](https://github.com/basalt-org/basalt/assets/46826967/83037770-a9e3-440d-bdca-f51af0aebee0)


## Quick Start

Try out the benchmarks yourself:

```
mojo -I . examples/housing.mojo
```
```
mojo -I . examples/sin_estimate.mojo
```
```
mojo -I . examples/mnist.mojo
```

Compare to the alternative PyTorch implementation:  
Make sure to install the requirements in `python-requirements.txt` in your python environment.

```
python examples/housing.py
python examples/sin_estimate.py
python examples/mnist.py
```

## Roadmap

### v0.1.0 ✅
- [x] Improve matrix multiplication and convolution kernels
- [x] Switch to custom Tensor and TensorShape implementations
- [x] Improve benchmarks and overall model execution performance
- [x] Add profiling and additional performance tests

### v0.2.0 (WIP)
- [ ] Add additional operators: Slice, (Un)Squeeze, Concat, Clip, Gather, Split, FMA ...
- [ ] Better layer support and more activation functions
- [ ] Graph submodules & graph concatenation
- [ ] Computer vision benchmark. 

### Long-Term
- [ ] Better parallelization
- [ ] GPU support
- [ ] Reworked Dataloader
- [ ] Autotuning and related features
- [ ] Graph compilation optimizations
- [ ] Operator fusion
- [ ] ONNX / Max compatibility

## Contributing

Basalt is built by community efforts and relies on your expertise and enthousiasm!  
Small fixes and improvements are much appreciated. If you are considering larger contributions, feel free to contact us for a smoother communication channel on Discord. If you find a bug or have an idea for a feature, please use our issue tracker. Before creating a new issue, please:
* Check if the issue already exists. If an issue is already reported, you can contribute by commenting on the existing issue.
* If not, create a new issue and include all the necessary details to understand/recreate the problem or feature request.

### Creating A Pull Request

1. Fork the Project
2. Create your Feature Branch
3. Commit your Changes
4. Push to the Branch
5. Open a Pull Request
> Once your changes are pushed, navigate to your fork on GitHub. And create a pull request against the original basalt-org/basalt repository.
> - Before creating a PR make sure it doesn't break any of the unit-tests. (e.g. `mojo run -I . test/test_ops.mojo`)
> - Introducing new big features requires a new test!
> - In the pull request, provide a detailed description of the changes and why they're needed. Link any relevant issues.
> - If there are any specific instructions for testing or validating your changes, include those as well.

## License

Distributed under the Apache 2.0 License with LLVM Exceptions. See [LICENSE](https://github.com/Basalt-Org/Basalt/blob/main/LICENSE) and the LLVM [License](https://llvm.org/LICENSE.txt) for more information.

## Acknowledgements

* Built with [Mojo](https://github.com/modularml/mojo) created by [Modular](https://github.com/modularml)


--- basalt/__init__.mojo ---

from .autograd import Graph, Symbol, OP
from .nn import Tensor, TensorShape
from basalt.utils.collection import Collection

alias dtype = DType.float32
alias nelts = 2 * simdwidthof[dtype]()
alias seed = 42
alias epsilon = 1e-12


--- basalt/autograd/__init__.mojo ---

from .symbol import Symbol
from .graph import Graph
from .ops import OP


--- basalt/autograd/attributes.mojo ---

from collections import Optional, OptionalReg
from utils.static_tuple import StaticTuple

from basalt.nn.tensor import Tensor, TensorShape, MAX_RANK
from basalt.utils.bytes import Bytes, scalar_to_bytes, bytes_to_scalar


alias MAX_ATTRS = 10
alias MAX_NAME_CHARS = 16
alias MAX_DATA_BYTES = 32


@register_passable("trivial")
struct AttributeType(Stringable):
    alias BOOL = AttributeType(0, "BOOL")
    alias INT = AttributeType(1, "INT")
    alias FLOAT = AttributeType(2, "FLOAT")
    alias STRING = AttributeType(3, "STRING")
    alias INTS = AttributeType(4, "INTS")
    alias FLOATS = AttributeType(5, "FLOATS")

    var id: UInt8
    var name: Bytes[MAX_NAME_CHARS]

    fn __init__(inout self, id: UInt8, name: String):
        self.id = id
        self.name = Bytes[MAX_NAME_CHARS](name)

    fn __init__(inout self, type: DType):
        if type.is_floating_point():
            self = AttributeType.FLOAT
        elif type.is_bool():
            self = AttributeType.BOOL
        else:
            self = AttributeType.INT

    fn __eq__(self, other: Self) -> Bool:
        return self.id == other.id

    fn __str__(self) -> String:
        return str(self.name)


@register_passable("trivial")
struct AttributeVector(Sized, Stringable, CollectionElement):
    var attributes: StaticTuple[Attribute, MAX_ATTRS]
    var size: Int

    fn __init__(inout self, *attributes: Attribute):
        self.attributes = StaticTuple[Attribute, MAX_ATTRS](Attribute("", ""))
        self.size = len(attributes)
        for i in range(self.size):
            self.attributes[i] = attributes[i]

    @always_inline("nodebug")
    fn __len__(self) -> Int:
        return self.size

    @always_inline("nodebug")
    fn __getitem__(self, index: Int) -> Attribute:
        return self.attributes[index]

    @always_inline("nodebug")
    fn __getitem__(self, index: StringLiteral) -> OptionalReg[Attribute]:
        for i in range(self.size):
            if self.attributes[i].name == Bytes[MAX_NAME_CHARS](index):
                return self.attributes[i]
        return None

    fn __str__(self) -> String:
        var s: String = "["
        for i in range(self.size):
            s += str(self.attributes[i])
            if i < self.size - 1:
                s += ", "
        return s + "]"


@register_passable("trivial")
struct Attribute(Stringable, CollectionElement):
    var data_shape: StaticIntTuple[MAX_RANK]
    var name: Bytes[MAX_NAME_CHARS]
    var data: Bytes[MAX_DATA_BYTES]
    var type: AttributeType
    var size: Int

    fn __init__(inout self, name: String, value: String):
        self.data_shape = StaticIntTuple[MAX_RANK]()
        self.name = Bytes[MAX_NAME_CHARS](name)
        self.data = Bytes[MAX_DATA_BYTES](value)
        self.type = AttributeType.STRING
        self.size = len(value)

    fn __init__(inout self, name: String, value: TensorShape):
        self.data_shape = StaticIntTuple[MAX_RANK]()
        self.name = Bytes[MAX_NAME_CHARS](name)
        self.data = Bytes[MAX_DATA_BYTES]()
        self.type = AttributeType.INTS
        self.size = value.rank()

        for i in range(self.size):
            self.data_shape[i] = value._shape[i]

    fn __init__[N: Int](inout self, name: String, value: StaticIntTuple[N]):
        constrained[N < MAX_RANK, "Attribute rank must be less than MAX_RANK."]()

        self.data_shape = StaticIntTuple[MAX_RANK]()
        self.name = Bytes[MAX_NAME_CHARS](name)
        self.data = Bytes[MAX_DATA_BYTES]()
        self.type = AttributeType.INTS
        self.size = N

        for i in range(self.size):
            self.data_shape[i] = value[i]

    fn __init__[dtype: DType](inout self, name: String, value: Scalar[dtype]):
        constrained[dtype.is_numeric(), "Attribute value must be numeric."]()

        self.data_shape = StaticIntTuple[MAX_RANK]()
        self.name = Bytes[MAX_NAME_CHARS](name)
        self.data = scalar_to_bytes[dtype, MAX_DATA_BYTES](value)
        self.type = AttributeType(dtype)
        self.size = 1

    fn __init__(inout self, name: String, value: Int):
        self.__init__(name, Int64(value))
        self.data_shape[0] = 1

    fn __init__(inout self, name: String, value: FloatLiteral):
        self.__init__(name, Float64(value))
        self.data_shape[0] = 1

    @always_inline("nodebug")
    fn __str__(self) -> String:
        return "Attribute(" + str(self.name) + ", " + "..." + ")"

    @always_inline("nodebug")
    fn to_string(self) -> String:
        return str(self.data)

    @always_inline("nodebug")
    fn to_shape(self) -> TensorShape:
        return TensorShape(rank=self.size, shape=self.data_shape)

    @always_inline("nodebug")
    fn to_static[N: Int](self) -> StaticIntTuple[N]:
        constrained[N < MAX_RANK, "Attribute rank must be less than MAX_RANK."]()

        var result = StaticIntTuple[N]()

        for i in range(N):
            result[i] = int(self.data_shape[i])

        return result

    @always_inline("nodebug")
    fn to_scalar[dtype: DType](self) -> Scalar[dtype]:
        constrained[dtype.is_numeric(), "Attribute value must be numeric."]()

        return bytes_to_scalar[dtype](self.data)

    @always_inline("nodebug")
    fn to_int(self) -> Int:
        return int(self.to_scalar[DType.int64]())

    fn json(self) -> String:
        var result = '{"name": "' + str(self.name) + '", '

        var type: String = ""
        var value: String = ""

        if self.type == AttributeType.STRING:
            type = "STRING"
            value = '"' + self.to_string() + '"'
        elif self.type == AttributeType.INTS:
            type = "INTS"

            var value_temp = self.to_shape()
            value = "["
            for i in range(value_temp.rank()):
                value += str(value_temp._shape[i])
                if i < value_temp.rank() - 1:
                    value += ", "
            value += "]"
        elif self.type == AttributeType.FLOAT:
            type = "FLOAT"
            value = str(self.to_scalar[DType.float64]())
        elif self.type == AttributeType.INT:
            type = "INT"
            value = str(self.to_int())
        else:
            type = "UNKNOWN"
            value = "UNKNOWN"

        result += '"type": "' + type + '", ' + '"value": ' + value

        return result + "}"


--- basalt/autograd/graph.mojo ---

from python.python import Python
from collections.optional import Optional, OptionalReg

from .node import Node
from .attributes import AttributeVector, Attribute
from .symbol import Symbol
from .ops import OP, static_result_shape, dynamic_result_shape
from .params import ParamDict, Param

from basalt import seed, dtype
from basalt import Tensor, TensorShape


struct Graph:
    var inputs: List[Symbol]
    var params: ParamDict
    var nodes: List[Node]
    var outputs: List[Symbol]
    var loss_out: OptionalReg[Symbol]
    var symbol_count: UInt32

    fn __init__(inout self):
        self.inputs = List[Symbol]()
        self.params = ParamDict()
        self.nodes = List[Node]()
        self.outputs = List[Symbol]()
        self.loss_out = None
        self.symbol_count = 0

    fn __moveinit__(inout self, owned other: Graph):
        self.inputs = other.inputs^
        self.params = other.params^
        self.nodes = other.nodes^
        self.outputs = other.outputs^
        self.loss_out = other.loss_out
        self.symbol_count = other.symbol_count

    fn create_symbol(inout self, shape: TensorShape, data: Optional[Param] = None, trainable: Bool = False, is_input: Bool = False) -> Symbol:
        var symbol = Symbol(self.symbol_count, dtype, shape, trainable)
        self.symbol_count += 1

        if is_input:
            self.inputs.append(symbol)
        else:
            if data is not None:
                self.params.put(symbol, data.value()[])
            else:
                self.params.put(symbol)

        return symbol

    fn input(inout self, shape: TensorShape, trainable: Bool = False) -> Symbol:
        return self.create_symbol(shape, trainable=trainable, is_input=True)

    fn param(inout self, shape: TensorShape, init: Param, trainable: Bool = True) -> Symbol:
        return self.create_symbol(shape, init, trainable)

    fn param(inout self, shape: TensorShape, trainable: Bool = True) -> Symbol:
        return self.create_symbol(shape, trainable=trainable)

    fn scalar(inout self, value: Scalar[dtype]) -> Symbol:
        return self.create_symbol(TensorShape(1), Param(value), trainable=False)

    fn constant(inout self, shape: TensorShape, data: List[Scalar[dtype]]) -> Symbol:
        return self.create_symbol(shape, Param(data), trainable=False)

    fn out(inout self, symbol: Symbol):
        self.outputs.append(symbol)

    fn loss(inout self, symbol: Symbol):
        self.loss_out = symbol

    fn op(
        inout self,
        op: OP,
        *operands: Symbol,
        attributes: AttributeVector = AttributeVector(),
    ) -> Symbol:
        var res_shape = static_result_shape(op, operands, attributes)
        var res = Symbol(self.symbol_count, dtype, res_shape, self.result_trainable(operands))
        self.symbol_count += 1

        var inputs = List[Symbol]()
        inputs.reserve(len(operands))

        for operand in operands:
            inputs.append(operand)

        self.nodes.append(Node(op, inputs, List[Symbol](res), attributes))
        return res

    fn op(
        inout self,
        op: OP,
        operand_1: Symbol,
        operand_2: Float64,
        attributes: AttributeVector = AttributeVector(),
    ) -> Symbol:
        return self.op(op, operand_1, self.scalar(operand_2), attributes=attributes)

    fn op(
        inout self,
        op: OP,
        operand_1: Float64,
        operand_2: Symbol,
        attributes: AttributeVector = AttributeVector(),
    ) -> Symbol:
        return self.op(op, self.scalar(operand_1), operand_2, attributes=attributes)

    fn create_symbols(inout self, shapes: List[TensorShape], trainable: Bool = False) -> List[Symbol]:
        var symbols = List[Symbol]()
        symbols.reserve(len(shapes))
        
        for shape in shapes:
            symbols.append(Symbol(self.symbol_count, dtype, shape[], trainable))
            self.symbol_count += 1
        
        return symbols

    fn add_node(inout self, op: OP, inputs: List[Symbol], outputs: List[Symbol], attributes: AttributeVector):
        self.nodes.append(Node(op, inputs, outputs, attributes))

    fn concat(inout self, *operands: Symbol, dim: Int = 0) -> Symbol:
        var attributes = AttributeVector(Attribute("dim", dim))
        var res_shape = dynamic_result_shape(OP.CONCAT, operands, attributes)[0]
        var res_symbols = self.create_symbols(List[TensorShape](res_shape), self.result_trainable(operands))

        var operand_list = List[Symbol]()
        operand_list.reserve(len(operands))
        for operand in operands:
            operand_list.append(operand)

        self.add_node(OP.CONCAT, operand_list, res_symbols, attributes)
        return res_symbols[0]

    fn split(
        inout self, operand: Symbol, sections: List[Int], dim: Int = 0
    ) -> List[Symbol]:
        var attributes = AttributeVector(Attribute("sections", TensorShape(sections)), Attribute("dim", dim))
        var res_shapes = dynamic_result_shape(OP.SPLIT, operand, attributes)
        var trainable = self.result_trainable(operand)
        var result_symbols = self.create_symbols(res_shapes, trainable)
        self.add_node(OP.SPLIT, List[Symbol](operand), result_symbols, attributes)
        return result_symbols

    @staticmethod
    fn result_trainable(operands: VariadicList[Symbol]) -> Bool:
        for operand in operands:
            if operand.trainable:
                return True
        return False

    fn json(self) -> String:
        var result: String = '{"graph_name": "basalt", "nodes": ['
        for i in range(len(self.nodes)):
            result += self.nodes[i].json()
            if i < len(self.nodes) - 1:
                result += ", "
        result += '], "inputs": ['
        for i in range(len(self.inputs)):
            result += self.inputs[i].json()
            if i < len(self.inputs) - 1:
                result += ", "
        result += '], "outputs": ['
        for i in range(len(self.outputs)):
            result += self.outputs[i].json()
            if i < len(self.outputs) - 1:
                result += ", "
        if self.loss_out:
            result += '], "loss": ['
            result += self.loss_out.value().json()
        result += '], "params": ['
        for i in range(len(self.params)):
            result += self.params.symbols[i].json()
            if i < len(self.params) - 1:
                result += ", "
        result += "]}"
        return result

    fn render(self, render_type: String = "node") raises:
        Python.add_to_path("./basalt/utils")
        var renderer = Python.import_module("graph_render")
        var json = Python.import_module("json")
        _ = renderer.netron_render(json.loads(self.json()), render_type)

    fn compile(inout self):
        # 0. Sorting the graph
        # The staticlly defined graph has an implicit topological sorted order because,
        # each new operation is added the list of nodes after its dependencies have been calculated.
        # This eliminates the need for explicit topological sorting.

        # Possibilities:
        # - 1. Graph layout transformation (graph rewrite)
        #       - Layer pruning (removing nodes that have no effect - with common sub-tree identification)
        #       - Eliminate redundant intermediate data copies
        #       - Operator replacement (e.g. replacing (combination of) costly ops with more efficient ones)
        #       - (exmple of graph rewrite: https://dl.acm.org/doi/pdf/10.1145/3453483.3454083  -  Table 4)
        #       - Other intra-block optimizations: (e.g. data layout transformation BCHW -> BHWC, etc.)
        # - 2. Operator fusion (combining ops without materializing intermediate results)
        #       - Fusion plan exploration
        #       - Fusion plan generation (with subsequent intra-block optimizations)
        #       - (example fusion plan algorithm: https://dl.acm.org/doi/pdf/10.1145/3453483.3454083   -   Listing 1)
        # - 3. Fusion Code generation (behaviour)
        #       - Code generation for planned fusion blocks
        #       - Other inter-block optimizations (e.g. data layout transformation BCHW -> BHWC, etc.)
        # - 4. Auto-tuning (of vectorization-, parallelization-, tiling-, unrolling-parameters)
        #       - (Might only work when memory is initialized)

        # Other considerations:
        # - Efficient Memory management:
        #       - Memory reuse (in-place operations)
        #       - Data layout from BCHW (batch, channel, height, width) to BHWC can lead to better utilization and efficiency
        # - VJP, JVP (for automatic differentiation)

        pass


--- basalt/autograd/node.mojo ---

from collections.optional import Optional
from utils.variant import Variant

from basalt.autograd import Symbol
from basalt.autograd.ops import OP

from .attributes import AttributeVector


@value
struct Node(CollectionElement, Stringable):
    var operator: OP
    var inputs: List[Symbol]
    var outputs: List[Symbol]
    var attributes: AttributeVector

    fn __init__(
        inout self,
        operator: OP,
        inputs: List[Symbol],
        outputs: List[Symbol],
        attributes: AttributeVector = AttributeVector(),
    ):
        self.operator = operator
        self.inputs = inputs
        self.outputs = outputs
        self.attributes = attributes

    fn __str__(self) -> String:
        return self.json()

    fn json(self) -> String:
        var s: String = '{"operator": "' + str(self.operator.name) + '", "inputs": ['
        for i in range(len(self.inputs)):
            s += self.inputs[i].json()
            if i < len(self.inputs) - 1:
                s += ", "
        s += '], "outputs": ['
        for i in range(len(self.outputs)):
            s += self.outputs[i].json()
            if i < len(self.outputs) - 1:
                s += ", "
        s += '], "attributes": ['
        for i in range(len(self.attributes)):
            s += self.attributes[i].json()
            if i < len(self.attributes) - 1:
                s += ", "
        s += "]}"
        return s


--- basalt/autograd/ops/__init__.mojo ---

from .ops import (
    OP,
    static_result_shape,
    dynamic_result_shape,
    forward_op,
    backward_op,
)


--- basalt/autograd/ops/basics.mojo ---

from math import log, exp
from algorithm import vectorize
from memory import memcpy
from utils.numerics import isinf

from basalt import Tensor, TensorShape
from basalt.nn.tensor import MAX_RANK
from basalt.utils.tensorutils import *
from basalt.autograd.attributes import Attribute, AttributeVector
from basalt.autograd.ops.matmul import dot, dot_transpose_t1, dot_transpose_t2
from basalt.utils.math_util import add, sub, mul, div


"""
Implement forward and backward operations for basic tensor manipulations.
"""


@value
struct ADD:
    @staticmethod
    fn result_shape(t1_shape: TensorShape, t2_shape: TensorShape) -> TensorShape:
        return broadcast_shapes(t1_shape, t2_shape)

    @staticmethod
    fn forward[
        t1_shape: TensorShape,
        t2_shape: TensorShape,
    ](inout res: Tensor[dtype], t1: Tensor[dtype], t2: Tensor[dtype]):
        """
        Forward pass of the add operation.
        """
        elwise_op[t1_shape, t2_shape, add](res, t1, t2)

    @staticmethod
    fn backward[
        tensor_id: Int,
        ug_shape: TensorShape,
        t1_shape: TensorShape,
        t2_shape: TensorShape,
    ](ug: Tensor[dtype], t1: Tensor[dtype], t2: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of element wise addition."""
        # d(x + y) / dx = d(x + y) / dy = 1
        return ug


@value
struct SUB:
    @staticmethod
    fn result_shape(t1_shape: TensorShape, t2_shape: TensorShape) -> TensorShape:
        return broadcast_shapes(t1_shape, t2_shape)

    @staticmethod
    fn forward[
        t1_shape: TensorShape,
        t2_shape: TensorShape,
    ](inout res: Tensor[dtype], t1: Tensor[dtype], t2: Tensor[dtype]):
        """
        Forward pass of the subtraction operation.
        """
        elwise_op[t1_shape, t2_shape, sub](res, t1, t2)

    @staticmethod
    fn backward[
        tensor_id: Int,
        ug_shape: TensorShape,
        t1_shape: TensorShape,
        t2_shape: TensorShape,
    ](ug: Tensor[dtype], t1: Tensor[dtype], t2: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of element wise subtraction."""

        # d(x - y) / dx = 1
        # d(x - y) / dy = -1
        @parameter
        if tensor_id == 0:
            return ug
        else:
            var res_grad = Tensor[dtype](ug_shape)
            elwise_op[mul](res_grad, ug, -1.0)
            return res_grad ^


@value
struct MUL:
    @staticmethod
    fn result_shape(t1_shape: TensorShape, t2_shape: TensorShape) -> TensorShape:
        return broadcast_shapes(t1_shape, t2_shape)

    @staticmethod
    fn forward[
        t1_shape: TensorShape,
        t2_shape: TensorShape,
    ](inout res: Tensor[dtype], t1: Tensor[dtype], t2: Tensor[dtype]):
        """
        Forward pass of the multiplication operation.
        """
        elwise_op[t1_shape, t2_shape, mul](res, t1, t2)

    @staticmethod
    fn backward[
        tensor_id: Int,
        ug_shape: TensorShape,
        t1_shape: TensorShape,
        t2_shape: TensorShape,
    ](ug: Tensor[dtype], t1: Tensor[dtype], t2: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of element wise multiplication."""

        # d(x * y) / dx = y
        # d(x * y) / dy = x
        @parameter
        if tensor_id == 0:
            var res_grad = Tensor[dtype](ug_shape)
            elwise_op[ug_shape, t2_shape, mul](res_grad, ug, t2)
            return res_grad ^
        else:
            var res_grad = Tensor[dtype](ug_shape)
            elwise_op[ug_shape, t1_shape, mul](res_grad, ug, t1)
            return res_grad ^


@value
struct DIV:
    @staticmethod
    fn result_shape(t1_shape: TensorShape, t2_shape: TensorShape) -> TensorShape:
        return broadcast_shapes(t1_shape, t2_shape)

    @staticmethod
    fn forward[
        t1_shape: TensorShape, t2_shape: TensorShape
    ](inout res: Tensor[dtype], t1: Tensor[dtype], t2: Tensor[dtype]):
        """
        Forward operation of element wise division.
        """
        elwise_op[t1_shape, t2_shape, div](res, t1, t2)

    @staticmethod
    fn backward[
        tensor_id: Int,
        ug_shape: TensorShape,
        t1_shape: TensorShape,
        t2_shape: TensorShape,
    ](ug: Tensor[dtype], t1: Tensor[dtype], t2: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of element wise division."""
        # d(x/y) / dx = 1/y
        # d(x/y) / dy = -x/y^2

        @parameter
        if tensor_id == 0:
            var res_grad = Tensor[dtype](ug_shape)
            elwise_op[ug_shape, t2_shape, div](res_grad, ug, t2)
            return res_grad ^
        else:
            alias broadcast = (t1_shape != t2_shape)
            alias is_scalar = (t2_shape == TensorShape(1))
            var res_grad = Tensor[dtype](ug_shape)

            @parameter
            if is_scalar:
                var factor: Scalar[dtype] = -1.0 / (t2[0] ** 2)

                @parameter
                fn vec_div_bw_scalar[nelts: Int](i: Int):
                    res_grad.store[nelts](
                        i, factor * t1.load[nelts](i) * ug.load[nelts](i)
                    )

                vectorize[vec_div_bw_scalar, nelts](ug_shape.num_elements())

            elif broadcast and not is_scalar:
                alias size = ug_shape.rank()
                alias strides1 = broadcast_calculate_strides[size, t1_shape, ug_shape]()
                alias strides2 = broadcast_calculate_strides[size, t2_shape, ug_shape]()

                @parameter
                fn vec_div_bw_broadcast[netls: Int](i: Int):
                    var index1 = get_real_index[size, strides1, ug_shape](i)
                    var index2 = get_real_index[size, strides2, ug_shape](i)
                    res_grad.store[nelts](
                        i,
                        -t1.load[nelts](index1)
                        / (t2.load[nelts](index2) ** 2)
                        * ug.load[nelts](i),
                    )

                vectorize[vec_div_bw_broadcast, 1](ug_shape.num_elements())

            else:

                @parameter
                fn vec_div_bw[nelts: Int](i: Int):
                    res_grad.store[nelts](
                        i,
                        -t1.load[nelts](i)
                        / (t2.load[nelts](i) ** 2)
                        * ug.load[nelts](i),
                    )

                vectorize[vec_div_bw, nelts](ug_shape.num_elements())

            return res_grad ^


@value
struct DOT:
    @staticmethod
    fn result_shape(t1_shape: TensorShape, t2_shape: TensorShape) -> TensorShape:
        return TensorShape(t1_shape[0], t2_shape[1])

    @staticmethod
    fn forward[
        t1_shape: TensorShape,
        t2_shape: TensorShape,
    ](inout res: Tensor[dtype], t1: Tensor[dtype], t2: Tensor[dtype]):
        """
        Forward pass of the dot operation.
        """
        dot[t1_shape, t2_shape](res, t1, t2)

    @staticmethod
    fn backward[
        tensor_id: Int,
        ug_shape: TensorShape,
        t1_shape: TensorShape,
        t2_shape: TensorShape,
    ](ug: Tensor[dtype], t1: Tensor[dtype], t2: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of dot product."""

        @parameter
        if tensor_id == 0:
            # dot(ug, t2.T)
            var res_grad = Tensor[dtype](t1_shape)
            dot_transpose_t2[ug_shape, t2_shape](res_grad, ug, t2)
            return res_grad ^
        else:
            # dot(t1.T, ug)
            var res_grad = Tensor[dtype](t2_shape)
            dot_transpose_t1[t1_shape, ug_shape](res_grad, t1, ug)
            return res_grad ^


@value
struct EXP:
    @staticmethod
    fn result_shape(t1_shape: TensorShape) -> TensorShape:
        return t1_shape

    @staticmethod
    fn forward[
        t1_shape: TensorShape,
    ](inout res: Tensor[dtype], t1: Tensor[dtype]):
        """Forward operation of exp."""
        elwise_transform[exp](res, t1)

    @staticmethod
    fn backward[
        ug_shape: TensorShape,
        t1_shape: TensorShape,
    ](ug: Tensor[dtype], t1: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of exp."""
        # d(exp(x)) / dx = exp(x)
        var res_grad = Tensor[dtype](ug_shape)

        @parameter
        fn vec_exp_bw[nelts: Int](i: Int):
            res_grad.store[nelts](i, exp(t1.load[nelts](i)) * ug.load[nelts](i))

        vectorize[vec_exp_bw, nelts](ug_shape.num_elements())
        return res_grad ^


@value
struct LOG:
    @staticmethod
    fn result_shape(t1_shape: TensorShape) -> TensorShape:
        return t1_shape

    @staticmethod
    fn forward[
        t1_shape: TensorShape,
    ](inout res: Tensor[dtype], t1: Tensor[dtype]):
        """Forward operation of exp."""
        elwise_transform[log](res, t1)

    @staticmethod
    fn backward[
        ug_shape: TensorShape,
        t1_shape: TensorShape,
    ](ug: Tensor[dtype], t1: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of log."""
        # d(log(x)) / dx = 1 / x
        var res_grad = Tensor[dtype](ug_shape)
        elwise_op[ug_shape, t1_shape, div](res_grad, ug, t1)
        return res_grad ^


struct POW:
    @staticmethod
    fn result_shape(t1_shape: TensorShape, t2_shape: TensorShape) -> TensorShape:
        # t2_shape == TensorShape(1)
        return t1_shape

    @staticmethod
    fn forward[
        t1_shape: TensorShape,
        t2_shape: TensorShape,
    ](inout res: Tensor[dtype], t1: Tensor[dtype], t2: Tensor[dtype]):
        """Forward operation of element wise pow."""
        # t2_shape is a graph scalar
        elwise_pow(res, t1, int(t2[0]))

    @staticmethod
    fn backward[
        tensor_id: Int,
        ug_shape: TensorShape,
        t1_shape: TensorShape,
        t2_shape: TensorShape,
    ](ug: Tensor[dtype], t1: Tensor[dtype], t2: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of element wise pow."""
        # d(x^y) / dx = y * x^(y-1)
        # d(x^y) / dy = sum( x^y * log(x) )
        var res_grad: Tensor[dtype]
        var a = t2[0]

        alias epsilon = 1e-12

        @parameter
        if tensor_id == 0:
            res_grad = Tensor[dtype](t1_shape)

            @parameter
            fn vec_pow_bw_x[nelts: Int](i: Int):
                res_grad.store[nelts](i, a * ((t1.load[nelts](i) + epsilon) ** (a - 1)) * ug.load[nelts](i))

            vectorize[vec_pow_bw_x, nelts](t1_shape.num_elements())

        else:
            # Gradient of the exponent
            res_grad = Tensor[dtype](t2_shape)  # t2_shape == TensorShape(1)

            @parameter
            fn vec_pow_bw_y[nelts: Int](i: Int):
                # the case when the value passed to log is 0.0
                var temp_log = log(t1.load[nelts](i))
                var temp_log_is_inf = isinf(temp_log)
                temp_log = temp_log_is_inf.select(0, temp_log)
                res_grad[0] += (
                    (t1.load[nelts](i) ** a)
                    * temp_log
                    * ug.load[nelts](i)
                ).reduce_add()

            vectorize[vec_pow_bw_y, nelts](ug_shape.num_elements())

        return res_grad ^


struct SUM:
    @staticmethod
    fn result_shape(t_shape: TensorShape, attributes: AttributeVector) -> TensorShape:
        var axis = attributes["axis"]

        if axis:
            return get_reduce_shape(t_shape, axis.value().to_int())
        else:
            return TensorShape(1)

    @staticmethod
    fn forward[
        t_shape: TensorShape, attributes: AttributeVector
    ](inout res: Tensor[dtype], t: Tensor[dtype]):
        """
        Forward pass of the sum operation.
        """

        alias axis = attributes["axis"]

        @parameter
        if axis:
            tsum(res, t, axis.value().to_int())
        else:
            res[0] = tsum(t)

    @staticmethod
    fn backward[
        ug_shape: TensorShape, t_shape: TensorShape, attributes: AttributeVector
    ](ug: Tensor[dtype], t: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of sum."""
        return Self.backward[ug_shape, t_shape](ug, t)

    @staticmethod
    fn backward[
        ug_shape: TensorShape, t_shape: TensorShape
    ](ug: Tensor[dtype], t: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of sum."""
        var res_grad = Tensor[dtype](t_shape)
        fill(res_grad, 1.0)

        elwise_op[t_shape, ug_shape, mul](res_grad, res_grad, ug)

        return res_grad ^


@value
struct MEAN:
    @staticmethod
    fn result_shape(t_shape: TensorShape, attributes: AttributeVector) -> TensorShape:
        var axis = attributes["axis"]

        if axis:
            return get_reduce_shape(t_shape, axis.value().to_int())
        else:
            return TensorShape(1)

    @staticmethod
    fn forward[
        t_shape: TensorShape, attributes: AttributeVector
    ](inout res: Tensor[dtype], t: Tensor[dtype]):
        """
        Forward pass of the mean operation.
        """

        alias axis = attributes["axis"]

        @parameter
        if axis:
            tmean(res, t, axis.value().to_int())
        else:
            res[0] = tmean(t)

    @staticmethod
    fn backward[
        ug_shape: TensorShape, t_shape: TensorShape, attributes: AttributeVector
    ](ug: Tensor[dtype], t: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of mean."""

        alias axis = attributes["axis"]

        @parameter
        if axis:
            return Self.backward[ug_shape, t_shape](ug, t, axis.value().to_int())
        else:
            return Self.backward[ug_shape, t_shape](ug, t)

    @staticmethod
    fn backward[
        ug_shape: TensorShape, t_shape: TensorShape
    ](ug: Tensor[dtype], t: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of mean."""
        # d(mean(t)) / dt = 1 / t.num_elements()
        var res_grad = Tensor[dtype](t_shape)

        var grad: Scalar[dtype] = 1.0 / t_shape.num_elements()

        grad = (
            grad * ug[0]
        )  # because ug is a tensor of size 1 when mean is used without an axis

        @parameter
        fn v_mean_d[nelts: Int](i: Int):
            res_grad.store[nelts](i, grad)

        vectorize[v_mean_d, nelts](t_shape.num_elements())

        return res_grad ^

    @staticmethod
    fn backward[
        ug_shape: TensorShape, t_shape: TensorShape
    ](ug: Tensor[dtype], t: Tensor[dtype], axis: Int) -> Tensor[dtype]:
        """Backward operation of mean."""
        # d(mean(t)) / dt = 1 / t.dim(axis)
        var res_grad = Tensor[dtype](t_shape)

        var grad: Scalar[dtype] = 1.0 / t_shape[axis]

        fill(res_grad, grad)

        elwise_op[t_shape, ug_shape, mul](res_grad, res_grad, ug)

        return res_grad ^


struct MAX:
    @staticmethod
    fn result_shape(t_shape: TensorShape, attributes: AttributeVector) -> TensorShape:
        var axis = attributes["axis"]

        if axis:
            return get_reduce_shape(t_shape, axis.value().to_int())
        else:
            return TensorShape(1)

    @staticmethod
    fn forward[
        t_shape: TensorShape, attributes: AttributeVector
    ](inout res: Tensor[dtype], t: Tensor[dtype]):
        """
        Forward pass of the max operation.
        """

        alias axis = attributes["axis"]

        @parameter
        if axis:
            tmax(res, t, axis.value().to_int())
        else:
            res[0] = tmax(t)

    @staticmethod
    fn backward[
        ug_shape: TensorShape, t_shape: TensorShape, attributes: AttributeVector
    ](ug: Tensor[dtype], t: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of max."""
        alias axis = attributes["axis"]

        @parameter
        if axis:
            return Self.backward[ug_shape, t_shape](ug, t, axis.value().to_int())
        else:
            return Self.backward[ug_shape, t_shape](ug, t)

    @staticmethod
    fn backward[
        ug_shape: TensorShape, t_shape: TensorShape
    ](ug: Tensor[dtype], t: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of max."""
        # This could be changed to something like in tinygrad:
        # max_1s = CMPEQ(original_tensor, expanded(max_tensor), axis=axis)
        # sum_max_1s = SUM(max_1s)
        # div_sum_max_1s = DIV(max_1, sum_max_1s)

        # The selected element gradient is 1.0, the others are 0.0. And if there are
        # multiple max values, the gradient is divided by the number of max
        # values (1/n) for each max value.

        var res_grad = Tensor[dtype](t_shape)

        # ug_shape size is 1
        var max_res = tmax(t)
        var sum_eq: Scalar[dtype] = 0
        for i in range(t.num_elements()):
            if t[i] == max_res:
                sum_eq += 1

        var factor = 1 / sum_eq
        for i in range(res_grad.num_elements()):
            if t[i] == max_res:
                res_grad[i] = factor * ug[0]

        return res_grad ^

    @staticmethod
    fn backward[
        ug_shape: TensorShape, t_shape: TensorShape
    ](ug: Tensor[dtype], t: Tensor[dtype], axis: Int) -> Tensor[dtype]:
        """Backward operation of max."""
        # The selected element gradient is 1.0, the others are 0.0. And if there are
        # multiple max values, the gradient is divided by the number of max
        # values (1/n) for each max value.

        var res_grad = Tensor[dtype](t_shape)
        var max_res = Tensor[dtype](ug_shape)
        alias strides = t_shape.strides()

        tmax(
            max_res, t, axis
        )  # To not calculate this again we could receive the result of the forward pass as a parameter

        for i in range(max_res.num_elements()):
            var index_base = (i % strides[axis]) + (i // strides[axis]) * (
                strides[axis] * t.dim(axis)
            )

            var count_1s: Scalar[dtype] = 0
            # Count the number of values equal to max_res
            for j in range(t.dim(axis)):
                var index = index_base + j * strides[axis]
                if t[index] == max_res[i]:
                    count_1s += 1
            # Divide 1.0 by the number of max values (n) and multiply by upper gradient value
            var factor = 1 / count_1s
            for j in range(t.dim(axis)):
                var index = index_base + j * strides[axis]
                if t[index] == max_res[i]:
                    res_grad[index] = factor * ug[i]

        return res_grad ^


struct TRANSPOSE:
    @staticmethod
    fn result_shape(t_shape: TensorShape, attributes: AttributeVector) -> TensorShape:
        var axes = attributes["axes"]  # axes to be permuted

        var rank = t_shape.rank()
        var shape = StaticIntTuple[MAX_RANK]()

        if axes:
            # NOTE: axis has to be the size of rank of the tensor
            var axes_shape = axes.value().to_shape()
            for i in range(rank):
                shape[i] = t_shape[axes_shape[i]]
        else:
            for i in range(rank):
                shape[i] = t_shape[rank - i - 1]

        return TensorShape(rank=rank, shape=shape)

    @staticmethod
    fn forward[
        t_shape: TensorShape, attributes: AttributeVector
    ](inout res: Tensor[dtype], t: Tensor[dtype]):
        """
        Forward pass of the transpose operation.
        """
        alias axes = attributes["axes"]

        @parameter
        if axes:
            var axes_shape = axes.value().to_shape()
            transpose(res, t, axes_shape)
        else:

            fn create_transpose_axes() -> TensorShape:
                var rank = t_shape.rank()
                var axes = StaticIntTuple[MAX_RANK]()
                for i in range(rank):
                    axes[i] = rank - i - 1
                return TensorShape(rank=rank, shape=axes)

            alias axes_shape = create_transpose_axes()

            transpose(res, t, axes_shape)

    @staticmethod
    fn backward[
        ug_shape: TensorShape, t_shape: TensorShape, attributes: AttributeVector
    ](ug: Tensor[dtype], t: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of transpose."""
        # No local gradient. Transpose is its own inverse.
        alias axes = attributes["axes"]

        var res_grad = Tensor[dtype](t_shape)

        @parameter
        if axes:

            fn create_inverse_axes() -> TensorShape:
                var axes_shape = axes.value().to_shape()

                var rank = axes_shape.rank()
                var axes_shape_inv = StaticIntTuple[MAX_RANK]()

                for i in range(rank):
                    axes_shape_inv[axes_shape[i]] = i

                return TensorShape(rank=rank, shape=axes_shape_inv)

            alias axes_shape_inv = create_inverse_axes()

            transpose(res_grad, ug, axes_shape_inv)
        else:

            fn create_transpose_axes() -> TensorShape:
                var rank = t_shape.rank()
                var axes = StaticIntTuple[MAX_RANK]()
                for i in range(rank):
                    axes[i] = rank - i - 1
                return TensorShape(axes)

            alias axes_shape_inv = create_transpose_axes()

            transpose(res_grad, ug, axes_shape_inv)

        return res_grad ^


struct FLATTEN:
    @staticmethod
    fn result_shape(t_shape: TensorShape) -> TensorShape:
        return TensorShape(t_shape.num_elements())

    @staticmethod
    fn forward[t_shape: TensorShape](inout res: Tensor[dtype], t: Tensor[dtype]):
        """
        Forward pass of the flatten operation.
        """
        memcpy(res.data(), t.data(), t_shape.num_elements())

    @staticmethod
    fn backward[
        ug_shape: TensorShape, t_shape: TensorShape
    ](ug: Tensor[dtype], t: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of flatten."""
        var res_grad = Tensor[dtype](t_shape)
        memcpy(res_grad.data(), ug.data(), ug_shape.num_elements())

        return res_grad ^


struct RESHAPE:
    @staticmethod
    fn result_shape(t_shape: TensorShape, attributes: AttributeVector) -> TensorShape:
        var new_shape = attributes["shape"]
        return new_shape.value().to_shape()

    @staticmethod
    fn forward[t_shape: TensorShape](inout res: Tensor[dtype], t: Tensor[dtype]):
        """
        Forward pass of the reshape operation.
        """
        memcpy(res.data(), t.data(), t_shape.num_elements())

    @staticmethod
    fn backward[
        ug_shape: TensorShape, t_shape: TensorShape
    ](ug: Tensor[dtype], t: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of reshape."""
        var res_grad = Tensor[dtype](t_shape)
        memcpy(res_grad.data(), ug.data(), ug_shape.num_elements())

        return res_grad ^


struct FMA:
    @staticmethod
    fn result_shape(
        t1_shape: TensorShape, t2_shape: TensorShape, t3_shape: TensorShape
    ) -> TensorShape:
        # FMA assumes: t1_shape == t2_shape == t3_shape
        # TODO: Error handling, constraints in API
        return t1_shape

    @staticmethod
    fn forward[
        t1_shape: TensorShape,
        t2_shape: TensorShape,
        t3_shape: TensorShape,
    ](
        inout res: Tensor[dtype],
        t1: Tensor[dtype],
        t2: Tensor[dtype],
        t3: Tensor[dtype],
    ):
        """
        Forward pass of the fma operation.
        """

        @parameter
        fn vec_fma[nelts: Int](i: Int):
            res.store[nelts](
                i, t1.load[nelts](i).fma(t2.load[nelts](i), t3.load[nelts](i))
            )

        vectorize[vec_fma, nelts, size = t1_shape.num_elements()]()

    @staticmethod
    fn backward[
        tensor_id: Int,
        ug_shape: TensorShape,
        t1_shape: TensorShape,
        t2_shape: TensorShape,
        t3_shape: TensorShape,
    ](
        ug: Tensor[dtype], t1: Tensor[dtype], t2: Tensor[dtype], t3: Tensor[dtype]
    ) -> Tensor[dtype]:
        """Backward operation of fma."""
        # d(x * y + z) / dx = y
        # d(x * y + z) / dy = x
        # d(x * y + z) / dz = 1

        @parameter
        if tensor_id == 0:
            var res_grad = Tensor[dtype](ug_shape)
            elwise_op[ug_shape, t2_shape, mul](res_grad, ug, t2)
            return res_grad ^
        elif tensor_id == 1:
            var res_grad = Tensor[dtype](ug_shape)
            elwise_op[ug_shape, t1_shape, mul](res_grad, ug, t1)
            return res_grad ^
        else:
            return ug


--- basalt/autograd/ops/conv.mojo ---

from basalt import Tensor, TensorShape
from basalt.autograd.attributes import AttributeVector

from algorithm import parallelize, vectorize, tile
from utils.loop import unroll


@always_inline
fn get_result_shape(
    input_shape: TensorShape,
    kernel_shape: TensorShape,
    padding: StaticIntTuple[2],
    stride: StaticIntTuple[2],
    dilation: StaticIntTuple[2],
) -> StaticIntTuple[2]:
    """
    Calculates the X and Y dimensions of the resulting convolution.
    Dimensions X, Y are on the end of the shape (..., X, Y)
        dimension X on index -2.
        dimension Y on index -1.
    """

    var result_x_dim = (
        (input_shape[-2] + (2 * padding[0]) - dilation[0] * (kernel_shape[-2] - 1) - 1)
        // stride[0]
    ) + 1
    var result_y_dim = (
        (input_shape[-1] + (2 * padding[1]) - dilation[1] * (kernel_shape[-1] - 1) - 1)
        // stride[1]
    ) + 1

    return StaticIntTuple[2](result_x_dim, result_y_dim)


struct CONV2D:
    @staticmethod
    fn result_shape(
        input_shape: TensorShape,
        kernel_shape: TensorShape,
        bias_shape: TensorShape,
        attributes: AttributeVector,
    ) -> TensorShape:
        # Output shape = [batch, out_channels, oX, oY]

        var padding = attributes["padding"].value().to_static[2]()
        var stride = attributes["stride"].value().to_static[2]()
        var dilation = attributes["dilation"].value().to_static[2]()
        var res = get_result_shape(input_shape, kernel_shape, padding, stride, dilation)

        return TensorShape(input_shape[0], kernel_shape[0], res[0], res[1])

    @staticmethod
    fn forward[
        input_shape: TensorShape,
        kernel_shape: TensorShape,
        bias_shape: TensorShape,
        attributes: AttributeVector,
    ](
        inout outputs: Tensor[dtype],
        inputs: Tensor[dtype],
        kernel: Tensor[dtype],
        bias: Tensor[dtype],
    ):
        """
        Performs a 2D convolution on the input tensor using the kernel and bias.
            inputs.shape     [batch, in_channels, iX, iY]
            kernel.shape     [out_channels, in_channels, kX, kY] (or weights)
            bias.shape       [out_channels].
            output.shape     [batch, out_channels, oX, oY].
        """
        alias padding = attributes["padding"].value().to_static[2]()
        alias stride = attributes["stride"].value().to_static[2]()
        alias dilation = attributes["dilation"].value().to_static[2]()

        alias padding_x = padding[0]
        alias padding_y = padding[1]
        alias stride_x = stride[0]
        alias stride_y = stride[1]
        alias dilation_x = dilation[0]
        alias dilation_y = dilation[1]

        alias batch_size = input_shape[0]
        alias in_channels = input_shape[1]
        alias in_x = input_shape[2]
        alias in_y = input_shape[3]
        alias out_channels = kernel_shape[0]
        alias k_x = kernel_shape[2]
        alias k_y = kernel_shape[3]
        alias out_x = output_shape[2]
        alias out_y = output_shape[3]
        alias col_x = out_x
        alias col_y = out_y

        alias col_shape = TensorShape(
            batch_size, col_x * col_y, in_channels * k_x * k_y
        )  # [batch, colX * colY, in_channels * kX * kY]
        alias output_shape = Self.result_shape(
            input_shape, kernel_shape, bias_shape, attributes
        )
        alias col_shape_stripped = TensorShape(in_channels * k_x * k_y, col_x, col_y)

        alias inputs_strides = input_shape.strides()
        alias kernel_strides = kernel_shape.strides()
        alias outputs_strides = output_shape.strides()
        alias col_strides = col_shape.strides()

        var col_ptr = DTypePointer[dtype].alloc(col_shape.num_elements())
        memset_zero(col_ptr, col_shape.num_elements())

        @parameter
        fn im2col(batch: Int):
            for ux in range(out_x):
                for uy in range(out_y):
                    for in_ch in range(in_channels):
                        for kx in range(k_x):
                            for ky in range(k_y):
                                var ix = ux * stride_x - padding_x + kx * dilation_x
                                var iy = uy * stride_y - padding_y + ky * dilation_y

                                if ix < 0 or iy < 0 or ix >= in_x or iy >= in_y:
                                    continue

                                var col_index = (
                                    batch * col_strides[0]
                                    + (ux * col_y + uy) * col_strides[1]
                                    + (in_ch * k_x * k_y + kx * k_y + ky)
                                )

                                var input_index = (
                                    batch * inputs_strides[0]
                                    + in_ch * inputs_strides[1]
                                    + ix * inputs_strides[2]
                                    + iy
                                )

                                col_ptr[col_index] = inputs[input_index]

        parallelize[im2col](batch_size)

        @parameter
        fn conv(batch: Int):
            for out_ch in range(out_channels):
                for ux in range(out_x):
                    for uy in range(out_y):
                        var result: SIMD[dtype, nelts] = 0

                        @parameter
                        fn v_im2col[_nelts: Int](in_ch_kx_ky: Int):
                            var col_index = (
                                batch * col_strides[0]
                                + (ux * col_y + uy) * col_strides[1]
                                + in_ch_kx_ky
                            )

                            var kernel_index = (
                                out_ch * kernel_strides[0] + in_ch_kx_ky
                            )

                            @parameter
                            if _nelts == nelts:
                                result += col_ptr.load[width=nelts](
                                    col_index
                                ) * kernel.load[nelts](kernel_index)
                            else:
                                result[0] += (
                                    col_ptr.load[width=_nelts](col_index)
                                    * kernel.load[_nelts](kernel_index)
                                ).reduce_add()

                        vectorize[v_im2col, nelts](in_channels * k_x * k_y)

                        var output_index = (
                            batch * outputs_strides[0]
                            + out_ch * outputs_strides[1]
                            + ux * outputs_strides[2]
                            + uy
                        )

                        outputs[output_index] = result.reduce_add() + bias[out_ch]

        parallelize[conv](batch_size)

        col_ptr.free()

    @staticmethod
    fn backward[
        tensor_id: Int,
        ug_shape: TensorShape,
        input_shape: TensorShape,
        kernel_shape: TensorShape,
        bias_shape: TensorShape,
        attributes: AttributeVector,
    ](
        ug: Tensor[dtype],
        inputs: Tensor[dtype],
        kernel: Tensor[dtype],
        bias: Tensor[dtype],
    ) -> Tensor[dtype]:
        """
        Backward operation of 2D convolution.

        Upper gradient of shape: [batch, out_channels, uX, uY].
        """

        alias padding = attributes["padding"].value().to_static[2]()
        alias stride = attributes["stride"].value().to_static[2]()
        alias dilation = attributes["dilation"].value().to_static[2]()
        alias padding_0 = padding[0]
        alias padding_1 = padding[1]
        alias stride_0 = stride[0]
        alias stride_1 = stride[1]
        alias dilation_0 = dilation[0]
        alias dilation_1 = dilation[1]

        alias inputs_strides = input_shape.strides()
        alias kernel_strides = kernel_shape.strides()
        alias ug_strides = ug_shape.strides()
        alias inputs_strides_0 = inputs_strides[0]
        alias inputs_strides_1 = inputs_strides[1]
        alias inputs_strides_2 = inputs_strides[2]
        alias kernel_strides_0 = kernel_strides[0]
        alias kernel_strides_1 = kernel_strides[1]
        alias kernel_strides_2 = kernel_strides[2]
        alias ug_strides_0 = ug_strides[0]
        alias ug_strides_1 = ug_strides[1]
        alias ug_strides_2 = ug_strides[2]

        alias input_shape_0 = input_shape[0]
        alias input_shape_1 = input_shape[1]
        alias input_shape_2 = input_shape[2]
        alias input_shape_3 = input_shape[3]
        alias kernel_shape_2 = kernel_shape[2]
        alias kernel_shape_3 = kernel_shape[3]
        alias ug_shape_0 = ug_shape[0]
        alias ug_shape_1 = ug_shape[1]
        alias ug_shape_2 = ug_shape[2]
        alias ug_shape_3 = ug_shape[3]

        var res: Tensor[dtype]

        @parameter
        if tensor_id == 0:
            # Inputs
            # Sum of upper gradient over batch, X, Y dimensions

            res = Tensor[dtype](input_shape)

            @parameter
            fn input_grad(batch: Int):
                for out_ch in range(ug_shape_1):
                    for ux in range(ug_shape_2):
                        for uy in range(ug_shape_3):  # For all the element of ug
                            var ix_base = ux * stride_0 - padding_0
                            var iy_base = uy * stride_1 - padding_1

                            var ug_val = ug[
                                batch * ug_strides_0
                                + out_ch * ug_strides_1
                                + ux * ug_strides_2
                                + uy
                            ]

                            for in_ch in range(input_shape_1):
                                for kx in range(kernel_shape_2):
                                    for ky in range(kernel_shape_3):
                                        var ix = ix_base + kx * dilation_0
                                        var iy = iy_base + ky * dilation_1

                                        if (
                                            ix < 0
                                            or iy < 0
                                            or ix >= input_shape_2
                                            or iy >= input_shape_3
                                        ):
                                            continue

                                        var kernel_index = (
                                            out_ch * kernel_strides_0
                                            + in_ch * kernel_strides_1
                                            + kx * kernel_strides_2
                                            + ky
                                        )

                                        var input_index = (
                                            batch * inputs_strides_0
                                            + in_ch * inputs_strides_1
                                            + ix * inputs_strides_2
                                            + iy
                                        )
                                        res[input_index] += (
                                            kernel[kernel_index] * ug_val
                                        )

            parallelize[input_grad](input_shape_0)

        elif tensor_id == 1:
            # Kernel
            # Sum of upper gradient over batch and X, Y dimensions
            res = Tensor[dtype](kernel_shape)

            @parameter
            fn kernel_grad(out_ch: Int):
                var channel_offset = out_ch * kernel_strides_0
                for k in range(input_shape_1 * kernel_shape_2 * kernel_shape_3):
                    var in_ch_kx_ky = divmod(k, kernel_shape_3)
                    var in_ch = k // (kernel_shape_2 * kernel_shape_3)
                    var kx = in_ch_kx_ky[0] % kernel_shape_2
                    var ky = in_ch_kx_ky[1]

                    # TODO: Cant vectorize since you are going different directions across input and upper grad
                    # But theoretically could transpose or split somehow
                    var result: Scalar[dtype] = 0
                    for batch in range(input_shape_0):
                        for ux in range(ug_shape_2):
                            for uy in range(ug_shape_3):
                                var ix = ux * stride_0 - padding_0 + kx * dilation_0
                                var iy = uy * stride_1 - padding_1 + ky * dilation_1

                                if (
                                    ix < 0
                                    or iy < 0
                                    or ix >= input_shape_2
                                    or iy >= input_shape_3
                                ):
                                    continue

                                var input_index = batch * inputs_strides_0 + in_ch * inputs_strides_1 + ix * inputs_strides_2 + iy
                                var ug_index = batch * ug_strides_0 + out_ch * ug_strides_1 + ux * ug_strides_2 + uy

                                result += inputs[input_index] * ug[ug_index]

                    var kernel_index = channel_offset + k
                    res[kernel_index] = result

            parallelize[kernel_grad](ug_shape_1)

        else:
            # Bias
            # Sum of upper gradient over batch and X, Y dimensions
            # out_channels == ug_shape[1] == bias_shape[0]
            res = Tensor[dtype](bias_shape)

            # Psuedocode
            # For every channel in the bias tensor,
            # Iterate over the upper gradient across the batch
            # For each batch, sum the upper gradient across X, Y dimensions
            # Add the sum to the bias tensor

            @parameter
            fn bias_grad(out_ch: Int):
                var channel_offset = out_ch * ug_strides_1
                var sum: Scalar[dtype] = 0
                for batch in range(ug_shape_0):
                    var batch_offset = batch * ug_strides_0 + channel_offset

                    @parameter
                    fn vec_sum[Nelts: Int](ux_uy: Int):
                        sum += ug.load[Nelts](batch_offset + ux_uy).reduce_add()

                    vectorize[vec_sum, nelts, size = ug_shape_2 * ug_shape_3]()

                res[out_ch] = sum

            parallelize[bias_grad](ug_shape_1)

        return res


--- basalt/autograd/ops/dynamics.mojo ---

from basalt import Symbol
from basalt.nn.model import Parameters
from ..attributes import AttributeVector


struct CONCAT:
    @staticmethod
    fn result_shape(
        input_shapes: List[TensorShape], attributes: AttributeVector
    ) -> List[TensorShape]:
        # Assumptions: all tensors have the same shape, except for the concatenating dimension
        var dim = attributes["dim"].value().to_int() if attributes["dim"] else 0

        var concat_size: Int = 0
        for i in range(len(input_shapes)):
            concat_size += input_shapes[i][dim]

        var res_shape = input_shapes[0]
        res_shape[dim] = concat_size

        return List[TensorShape](res_shape)

    @staticmethod
    fn calc_chunks(shape: TensorShape, dim: Int) -> Int:
        # Number of chunks up to the concatenating dimension
        # Assuming tensor of equal shape, except for the concatenating dimension
        var chunks = 1
        for i in range(dim):
            chunks *= shape[i]
        return chunks

    @staticmethod
    fn forward[attributes: AttributeVector](
        inputs: List[Symbol],
        outputs: List[Symbol],
        inout parameters: Parameters,
    ):
        alias dim = attributes["dim"].value().to_int() if attributes["dim"] else 0
        var n_chunks = Self.calc_chunks(inputs[0].shape, dim)

        var chunks = List[Int]()
        var chunk_offsets = List[Int](0)
        for i in range(len(inputs)):
            chunks.append(inputs[i].shape.num_elements() // n_chunks)
            chunk_offsets.append(chunk_offsets[i] + chunks[i])

        for i in range(n_chunks):
            for j in range(len(inputs)):
                memcpy(
                    parameters.tensors[outputs[0]].data()
                    + i * chunk_offsets[len(inputs)]
                    + chunk_offsets[j],
                    parameters.tensors[inputs[j]].data() + i * chunks[j],
                    chunks[j],
                )

    @staticmethod
    fn backward[input_id: Int, attributes: AttributeVector](
        inputs: List[Symbol],
        outputs: List[Symbol],
        inout parameters: Parameters,
    ) -> Tensor[dtype]:
        alias dim = attributes["dim"].value().to_int() if attributes["dim"] else 0
        var n_chunks = Self.calc_chunks(inputs[0].shape, dim)

        var chunks = List[Int]()
        var chunk_offsets = List[Int](0)
        for i in range(len(inputs)):
            chunks.append(inputs[i].shape.num_elements() // n_chunks)
            chunk_offsets.append(chunk_offsets[i] + chunks[i])

        var res_grad = Tensor[dtype](inputs[input_id].shape)
        for i in range(n_chunks):
            memcpy(
                res_grad.data() + i * chunks[input_id],
                parameters.grads[outputs[0]].data()
                + i * chunk_offsets[len(inputs)]
                + chunk_offsets[input_id],
                chunks[input_id],
            )

        return res_grad ^


struct SPLIT:
    @staticmethod
    fn result_shape(
        input_shapes: List[TensorShape], attributes: AttributeVector
    ) -> List[TensorShape]:
        # Assuming the sum of the sections is equal to the total size in the dim dimension.
        # E.g. sections = [5, 5, 2] -> shape (., 12, ., .) for dim = 1
        var dim = attributes["dim"].value().to_int() if attributes["dim"] else 0
        var sections = attributes["sections"].value().to_shape()

        var res_shapes = List[TensorShape]()
        for i in range(sections.rank()):
            var new_shape = input_shapes[0]
            new_shape[dim] = sections[i]
            res_shapes.append(new_shape)

        return res_shapes

    @staticmethod
    fn calc_chunks(shape: TensorShape, dim: Int) -> Int:
        # Number of chunks up to the concatenating dimension
        # Assuming tensor of equal shape, except for the concatenating dimension
        var chunks = 1
        for i in range(dim):
            chunks *= shape[i]
        return chunks

    @staticmethod
    fn forward[attributes: AttributeVector](
        inputs: List[Symbol],
        outputs: List[Symbol],
        inout parameters: Parameters,
    ):
        alias dim = attributes["dim"].value().to_int() if attributes["dim"] else 0
        alias sections = attributes["sections"].value().to_shape()
        var n_chunks = Self.calc_chunks(inputs[0].shape, dim)

        var chunks = List[Int]()
        var chunk_offsets = List[Int](0)
        for i in range(len(outputs)):
            chunks.append(outputs[i].shape.num_elements() // n_chunks)
            chunk_offsets.append(chunk_offsets[i] + chunks[i])

        for i in range(n_chunks):
            for j in range(len(outputs)):
                memcpy(
                    parameters.tensors[outputs[j]].data() + i * chunks[j],
                    parameters.tensors[inputs[0]].data()
                    + i * chunk_offsets[len(outputs)]
                    + chunk_offsets[j],
                    chunks[j],
                )

    @staticmethod
    fn backward[input_id: Int, attributes: AttributeVector](
        inputs: List[Symbol],
        outputs: List[Symbol],
        inout parameters: Parameters,
    ) -> Tensor[dtype]:
        alias dim = attributes["dim"].value().to_int() if attributes["dim"] else 0
        alias sections = attributes["sections"].value().to_shape()
        var n_chunks = Self.calc_chunks(inputs[0].shape, dim)

        var chunks = List[Int]()
        var chunk_offsets = List[Int](0)
        for i in range(len(outputs)):
            chunks.append(outputs[i].shape.num_elements() // n_chunks)
            chunk_offsets.append(chunk_offsets[i] + chunks[i])

        var res_grad = Tensor[dtype](inputs[input_id].shape)

        for i in range(n_chunks):
            for j in range(len(outputs)):
                memcpy(
                    res_grad.data()
                    + i * chunk_offsets[len(outputs)]
                    + chunk_offsets[j],
                    parameters.grads[outputs[j]].data() + i * chunks[j],
                    chunks[j],
                )

        return res_grad ^


--- basalt/autograd/ops/matmul.mojo ---

from basalt.utils.tensorutils import transpose_2D
from algorithm import vectorize, parallelize


@always_inline
fn calculate_block[
    M: Int, N: Int, K: Int, BLOCK_M: Int, BLOCK_N: Int, nelts: Int
](
    res: DTypePointer[dtype],
    t1: DTypePointer[dtype],
    t2: DTypePointer[dtype],
    bm: Int,
    bn: Int,
):
    # Compute tile
    var acc = stack_allocation[BLOCK_M * BLOCK_N, dtype]()
    memset_zero[dtype](acc, BLOCK_M * BLOCK_N)

    for k in range(K):

        @parameter
        for m in range(BLOCK_M):

            @parameter
            fn inner_n[nelts: Int](n: Int):
                acc.store[width=nelts](
                    m * BLOCK_N + n,
                    SIMD[dtype, nelts]
                    .splat(t1[(bm + m) * K + k])
                    .fma(
                        t2.load[width=nelts](k * N + (bn + n)),
                        acc.load[width=nelts](m * BLOCK_N + n),
                    ),
                )

            vectorize[inner_n, nelts](BLOCK_N)

    # Store tile
    for m in range(BLOCK_M):

        @parameter
        fn vec_store[nelts: Int](n: Int):
            res.store[width=nelts](
                (bm + m) * N + (bn + n), acc.load[width=nelts](m * BLOCK_N + n)
            )

        vectorize[vec_store, nelts](BLOCK_N)


@parameter
@always_inline
fn dot[
    t1_shape: TensorShape, t2_shape: TensorShape
](inout res: Tensor[dtype], t1: Tensor[dtype], t2: Tensor[dtype]):
    dot[t1_shape, t2_shape](res.data(), t1.data(), t2.data())


@parameter
@always_inline
fn dot[
    t1_shape: TensorShape, t2_shape: TensorShape
](res: DTypePointer[dtype], t1: DTypePointer[dtype], t2: DTypePointer[dtype]):
    alias M = t1_shape[0]  # t1[0]
    alias K = t1_shape[1]  # t1[1], t2[0]
    alias N = t2_shape[1]  # t2[1]

    # simdwidthof[dtype]() = 8 for float32
    alias nelts = simdwidthof[dtype]()
    alias BLOCK_N = 8 * 2
    alias BLOCK_M = 6
    alias THREADS = 6  # num_logical_cores()

    alias BLOCK_N_REMAINDER = N % BLOCK_N
    alias BLOCK_M_REMAINDER = M % BLOCK_M

    @parameter
    fn bm_par(m_outer: Int):
        var bm = m_outer * BLOCK_M

        for n_outer in range(0, N // BLOCK_N):
            var bn = n_outer * BLOCK_N

            calculate_block[M, N, K, BLOCK_M, BLOCK_N, nelts](res, t1, t2, bm, bn)

        # Handle the remainder of N
        @parameter
        if BLOCK_N_REMAINDER > 0:
            var bn = N - BLOCK_N_REMAINDER

            calculate_block[M, N, K, BLOCK_M, BLOCK_N_REMAINDER, nelts](
                res, t1, t2, bm, bn
            )

    parallelize[bm_par](M // BLOCK_M, M // BLOCK_M)

    # Handle the remainder of M
    @parameter
    if BLOCK_M_REMAINDER > 0:
        var bm = M - BLOCK_M_REMAINDER

        for n_outer in range(0, N // BLOCK_N):
            var bn = n_outer * BLOCK_N

            calculate_block[M, N, K, BLOCK_M_REMAINDER, BLOCK_N, nelts](
                res, t1, t2, bm, bn
            )

        # Handle corner remainder
        @parameter
        if BLOCK_N_REMAINDER > 0:
            var bn = N - BLOCK_N_REMAINDER

            calculate_block[M, N, K, BLOCK_M_REMAINDER, BLOCK_N_REMAINDER, nelts](
                res, t1, t2, bm, bn
            )


fn dot_transpose_t2[
    A_shape: TensorShape, B_shape: TensorShape
](inout C: DTypePointer[dtype], A: DTypePointer[dtype], B: DTypePointer[dtype]):
    dot[A_shape, TensorShape(B_shape[1], B_shape[0])](C, A, transpose_2D[B_shape](B))


fn dot_transpose_t2[
    A_shape: TensorShape, B_shape: TensorShape
](inout C: Tensor[dtype], A: Tensor[dtype], B: Tensor[dtype]):
    memset_zero[dtype](C.data(), C.num_elements())

    dot[A_shape, TensorShape(B_shape[1], B_shape[0])](C, A, transpose_2D[B_shape](B))

    # @parameter
    # fn calc_row(i: Int):
    #     for j in range(B_shape[0]):

    #         @parameter
    #         fn calc_row_A_B[nelts: Int](k: Int):
    #             var A_pos = i * A.dim(1) + k
    #             var B_pos = j * A.dim(1) + k
    #             var t_new_pos = i * C.dim(1) + j

    #             C[t_new_pos] += (
    #                 A.load[nelts](A_pos) * B.load[nelts](B_pos)
    #             ).reduce_add()

    #         vectorize[calc_row_A_B, nelts, size=A_shape[1]]()

    # parallelize[calc_row](A_shape[0], 1)


fn dot_transpose_t1[
    A_shape: TensorShape, B_shape: TensorShape
](inout C: Tensor[dtype], A: Tensor[dtype], B: Tensor[dtype]):
    memset_zero[dtype](C.data(), C.num_elements())

    dot[TensorShape(A_shape[1], A_shape[0]), B_shape](C, transpose_2D[A_shape](A), B)

    # @parameter
    # fn calc_row(i: Int):
    #     for j in range(A_shape[0]):

    #         @parameter
    #         fn calc_row_t_new_B[nelts: Int](k: Int):
    #             var A_pos = j * A.dim(1) + i
    #             var B_pos = j * B.dim(1) + k
    #             var t_new_pos = i * C.dim(1) + k

    #             C.store[nelts](
    #                 t_new_pos,
    #                 C.load[nelts](t_new_pos)
    #                 + A[A_pos] * B.load[nelts](B_pos),
    #             )

    #         vectorize[calc_row_t_new_B, nelts, size=B_shape[1]]()

    # parallelize[calc_row](A_shape[1], 1)


--- basalt/autograd/ops/mlops.mojo ---

from algorithm import vectorize, parallelize
from math import exp
from utils.numerics import min_finite, max_finite

from basalt import Tensor, TensorShape
from basalt.utils.tensorutils import elwise_transform
from basalt.autograd.attributes import Attribute, AttributeVector


@value
struct SIGMOID:
    @staticmethod
    fn result_shape(t1_shape: TensorShape) -> TensorShape:
        return t1_shape

    @staticmethod
    @always_inline
    fn sigmoid[
        type: DType, simd_width: Int
    ](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]:
        return 1 / (1 + exp(-x))

    @staticmethod
    @always_inline
    fn sidmoid_bw[
        type: DType, simd_width: Int
    ](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]:
        return Self.sigmoid(x) * (1 - Self.sigmoid(x))

    @staticmethod
    fn forward[
        t1_shape: TensorShape,
    ](inout res: Tensor[dtype], t1: Tensor[dtype]):
        """Forward operation of sigmoid."""
        elwise_transform[Self.sigmoid](res, t1)

    @staticmethod
    fn backward[
        ug_shape: TensorShape,
        t1_shape: TensorShape,
    ](ug: Tensor[dtype], t1: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of sigmoid."""
        # d(sigmod(x))/dx = sigmoid(x) * (1 - sigmoid(x))
        var res_grad = Tensor[dtype](ug_shape)

        @parameter
        fn vec_sigmoid_bw[nelts: Int](idx: Int):
            res_grad.store[nelts](
                idx,
                Self.sidmoid_bw(t1.load[nelts](idx)) * ug.load[nelts](idx),
            )

        vectorize[vec_sigmoid_bw, nelts](ug_shape.num_elements())

        return res_grad^


struct RELU:
    @staticmethod
    fn result_shape(t1_shape: TensorShape) -> TensorShape:
        return t1_shape

    @staticmethod
    @always_inline
    fn relu[
        type: DType, simd_width: Int
    ](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]:
        # x if x > 0 else 0
        return (x > 0).select(x, 0)

    @staticmethod
    @always_inline
    fn relu_bw[
        type: DType, simd_width: Int
    ](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]:
        # 1 if x > 0 else 0
        return (x > 0).select[type](1, 0)

    @staticmethod
    fn forward[
        t1_shape: TensorShape,
    ](inout res: Tensor[dtype], t1: Tensor[dtype]):
        """Forward operation of relu."""
        elwise_transform[Self.relu](res, t1)

    @staticmethod
    fn backward[
        ug_shape: TensorShape,
        t1_shape: TensorShape,
    ](ug: Tensor[dtype], t1: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of relu."""
        # d(relu(x))/dx = 1 if x > 0 else 0. We also give 0 to x = 0 instead of undefined.
        var res_grad = Tensor[dtype](ug_shape)

        @parameter
        fn vec_relu_bw[nelts: Int](idx: Int):
            res_grad.store[nelts](
                idx, Self.relu_bw(t1.load[nelts](idx)) * ug.load[nelts](idx)
            )

        vectorize[vec_relu_bw, nelts](ug_shape.num_elements())

        return res_grad^


struct LEAKYRELU:
    @staticmethod
    fn result_shape(t1_shape: TensorShape) -> TensorShape:
        return t1_shape

    @staticmethod
    fn forward[
        t1_shape: TensorShape,
        attributes: AttributeVector,
    ](inout res: Tensor[dtype], t1: Tensor[dtype]):
        """Forward operation of leaky_relu."""

        fn leaky_relu[
            type: DType,
            simd_width: Int,
        ](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]:
            var negative_slope = attributes["negative_slope"].value().to_scalar[
                type
            ]()
            return (x > 0).select(x, x * negative_slope)

        elwise_transform[leaky_relu](res, t1)

    @staticmethod
    fn backward[
        ug_shape: TensorShape,
        t1_shape: TensorShape,
        attributes: AttributeVector,
    ](ug: Tensor[dtype], t1: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of leaky_relu."""

        @always_inline
        fn leaky_relu_bw[
            type: DType, simd_width: Int
        ](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]:
            var negative_slope = attributes["negative_slope"].value().to_scalar[
                type
            ]()

            return (x > 0).select[type](1, negative_slope)

        var res_grad = Tensor[dtype](ug_shape)

        @parameter
        fn vec_leaky_relu_bw[nelts: Int](idx: Int):
            res_grad.store[nelts](
                idx,
                leaky_relu_bw(t1.load[nelts](idx)) * ug.load[nelts](idx),
            )

        vectorize[vec_leaky_relu_bw, nelts](ug_shape.num_elements())

        return res_grad^


struct TANH:
    @staticmethod
    fn result_shape(t1_shape: TensorShape) -> TensorShape:
        return t1_shape

    @staticmethod
    @always_inline
    fn tanh[
        type: DType, simd_width: Int
    ](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]:
        return (exp(x) - exp(-x)) / (exp(x) + exp(-x))

    @staticmethod
    @always_inline
    fn tanh_bw[
        type: DType, simd_width: Int
    ](x: SIMD[type, simd_width]) -> SIMD[type, simd_width]:
        return 1 - pow(Self.tanh(x), 2)

    @staticmethod
    fn forward[
        t1_shape: TensorShape,
    ](inout res: Tensor[dtype], t1: Tensor[dtype]):
        """Forward operation of tanh."""
        elwise_transform[Self.tanh](res, t1)

    @staticmethod
    fn backward[
        ug_shape: TensorShape,
        t1_shape: TensorShape,
    ](ug: Tensor[dtype], t1: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of tanh."""
        # d(tanh(x))/dx = 1 - tanh(x) ** 2
        var res_grad = Tensor[dtype](ug_shape)

        @parameter
        fn vec_tanh_bw[nelts: Int](idx: Int):
            res_grad.store[nelts](
                idx, Self.tanh_bw(t1.load[nelts](idx)) * ug.load[nelts](idx)
            )

        vectorize[vec_tanh_bw, nelts](ug_shape.num_elements())

        return res_grad^


struct CLIP:
    @staticmethod
    fn result_shape(t_shape: TensorShape) -> TensorShape:
        return t_shape

    @staticmethod
    fn forward[
        t_shape: TensorShape, attributes: AttributeVector
    ](inout res: Tensor[dtype], t: Tensor[dtype]):
        """
        Forward pass of the clip operation.
        """
        alias min_attr = attributes["min"]
        alias max_attr = attributes["max"]

        var min_val = min_attr.value().to_scalar[
            dtype
        ]() if min_attr else min_finite[dtype]()
        var max_val = max_attr.value().to_scalar[
            dtype
        ]() if max_attr else max_finite[dtype]()

        @parameter
        fn vec_clip[nelts: Int](i: Int):
            res.store[nelts](i, t.load[nelts](i).min(max_val).max(min_val))

        vectorize[vec_clip, nelts, size = t_shape.num_elements()]()

    @staticmethod
    fn backward[
        ug_shape: TensorShape,
        t_shape: TensorShape,
        attributes: AttributeVector = AttributeVector(),
    ](ug: Tensor[dtype], t: Tensor[dtype]) -> Tensor[dtype]:
        """Backward operation of clip."""
        alias min_attr = attributes["min"]
        alias max_attr = attributes["max"]

        var min_val = min_attr.value().to_scalar[
            dtype
        ]() if min_attr else min_finite[dtype]()
        var max_val = max_attr.value().to_scalar[
            dtype
        ]() if max_attr else max_finite[dtype]()

        var res_grad = Tensor[dtype](t_shape)

        @parameter
        fn vec_clip_bw[nelts: Int](i: Int):
            var val = t.load[nelts](i)
            res_grad.store[nelts](
                i,
                ((val >= min_val) * (val <= max_val)).select(
                    ug.load[nelts](i), 0
                ),
            )

        vectorize[vec_clip_bw, nelts, size = t_shape.num_elements()]()

        return res_grad^


struct SQUEEZE:
    @staticmethod
    fn result_shape(
        t1_shape: TensorShape, attributes: AttributeVector
    ) -> TensorShape:
        var dim = attributes["dims"]
        var dims_to_squeeze = dim.value().to_shape() if dim else TensorShape()

        var new_shape = List[Int]()
        for i in range(t1_shape.rank()):
            if (not dim and t1_shape[i] == 1) or (
                i in dims_to_squeeze and t1_shape[i] == 1
            ):
                continue
            new_shape.append(t1_shape[i])

        return TensorShape(new_shape)

    @staticmethod
    fn forward[
        t1_shape: TensorShape,
        attributes: AttributeVector,
    ](inout res: Tensor[dtype], t1: Tensor[dtype]):
        memcpy(res.data(), t1.data(), t1.num_elements())

    @staticmethod
    fn backward[
        ug_shape: TensorShape,
        t1_shape: TensorShape,
    ](ug: Tensor[dtype], t1: Tensor[dtype]) -> Tensor[dtype]:
        var res_grad = Tensor[dtype](t1_shape)
        memcpy(res_grad.data(), ug.data(), ug.num_elements())
        return res_grad^


struct UNSQUEEZE:
    @staticmethod
    fn result_shape(
        t1_shape: TensorShape, attributes: AttributeVector
    ) -> TensorShape:
        var dim = attributes["dims"]
        var dims_to_squeeze = dim.value().to_shape() if dim else TensorShape()

        # Position in the expanded dims where the new dim (or dims) is placed.
        var new_rank = t1_shape.rank() + dims_to_squeeze.rank()

        var new_shape = List[Int]()
        var j = 0
        for i in range(new_rank):
            if i in dims_to_squeeze or i - new_rank in dims_to_squeeze:
                new_shape.append(1)
            else:
                new_shape.append(t1_shape[j])
                j += 1

        return TensorShape(new_shape)

    @staticmethod
    fn forward[
        t1_shape: TensorShape,
        attributes: AttributeVector,
    ](inout res: Tensor[dtype], t1: Tensor[dtype]):
        memcpy(res.data(), t1.data(), t1.num_elements())

    @staticmethod
    fn backward[
        ug_shape: TensorShape,
        t1_shape: TensorShape,
    ](ug: Tensor[dtype], t1: Tensor[dtype]) -> Tensor[dtype]:
        var res_grad = Tensor[dtype](t1_shape)
        memcpy(res_grad.data(), ug.data(), ug.num_elements())
        return res_grad^


struct SLICE:
    @staticmethod
    fn adjust_boundary(slice: Int, dim_size: Int) -> Int:
        # Adjust negative indices & ensure they are within bounds.
        var s = slice if slice >= 0 else dim_size + slice
        return max(min(s, dim_size), 0)

    @staticmethod
    fn default_starts(shape: TensorShape) -> List[Int]:
        var starts = List[Int]()
        for i in range(shape.rank()):
            starts.append(0)
        return starts^

    @staticmethod
    fn default_ends(shape: TensorShape) -> List[Int]:
        var ends = List[Int]()
        for i in range(shape.rank()):
            ends.append(shape[i])
        return ends^

    @staticmethod
    fn default_steps(shape: TensorShape) -> List[Int]:
        var steps = List[Int]()
        for i in range(shape.rank()):
            steps.append(1)
        return steps^

    @staticmethod
    fn default_axes(shape: TensorShape) -> List[Int]:
        # NOTE: axes can't be negative
        var axes = List[Int]()
        for i in range(shape.rank()):
            axes.append(i)
        return axes^

    @staticmethod
    fn result_shape(
        t1_shape: TensorShape, attributes: AttributeVector
    ) -> TensorShape:
        # NOTE: Starts and ends have to be of the same size
        # NOTE: If axes not provided, starts and ends have to be of the same size as t1_shape
        var starts = attributes["starts"].value().to_shape()
        var ends = attributes["ends"].value().to_shape()
        var steps = attributes["steps"].value().to_shape() if attributes[
            "steps"
        ] else Self.default_steps(starts)
        var axes = attributes["axes"].value().to_shape() if attributes[
            "axes"
        ] else Self.default_axes(t1_shape)

        var new_shape = t1_shape
        for i in range(starts.rank()):
            var axis = axes[i]
            new_shape[axis] = len(
                range(
                    start=Self.adjust_boundary(starts[i], t1_shape[axis]),
                    end=Self.adjust_boundary(ends[i], t1_shape[axis]),
                    step=steps[i],
                )
            )

        return new_shape

    @staticmethod
    fn reorder_positions[
        id: Int
    ](original: TensorShape, axes: TensorShape, t1_shape: TensorShape) -> List[
        Int
    ]:
        # Reorder the starts (id=0), ends (id=1) or steps (id=2) to match the order of the axes
        var updated: List[Int]

        @parameter
        if id == 0:
            updated = Self.default_starts(t1_shape)
        elif id == 1:
            updated = Self.default_ends(t1_shape)
        else:
            updated = Self.default_steps(t1_shape)

        for i in range(axes.rank()):
            var axis = axes[i]
            updated[axis] = original[i] if id == 2 else Self.adjust_boundary(
                original[i], t1_shape[axis]
            )

        return updated^

    # NOTE: For now you can't have recursive function as parameter functions.
    # NOTE: From testing it seems a recursive function is almost the same speed as doing multiple nested for loops.
    @staticmethod
    fn recursive_iters_slice[
        shape: TensorShape,
        original_shape: TensorShape,
        steps: List[Int],
        starts: List[Int],
        ends: List[Int],
        backward_op: Bool = False,
    ](
        inout res: Tensor[dtype],
        t1: Tensor[dtype],
        last_dims: Int,
        position: Int,
        last_position: Int,
        idx: Int,
        idx_original: Int,
    ):
        alias strides = shape.strides()
        alias t1_strides = original_shape.strides()

        var idx_temp = idx
        var idx_original_temp = starts[position] * t1_strides[
            position
        ] + idx_original

        if position == last_position + 1:
            # Work on the last dimensions
            alias position = shape.rank() - 1
            alias stride = t1_strides[position] * steps[position]

            @parameter
            fn v_slice[nelts: Int](k: Int):
                @parameter
                if not backward_op:

                    @parameter
                    if steps[position] == 1:
                        res.store[nelts](
                            idx_temp + k, t1.load[nelts](idx_original_temp)
                        )
                    else:
                        res.store[nelts](
                            idx_temp + k,
                            t1.data()
                            .offset(idx_original_temp)
                            .simd_strided_load[nelts](stride),
                        )
                else:

                    @parameter
                    if steps[position] == 1:
                        res.store[nelts](idx_original_temp, t1.load[nelts](idx_temp + k))
                    else:
                        res.data().offset(idx_original_temp).simd_strided_store[width=nelts](
                            t1.load[nelts](idx_temp + k),
                            stride
                        )

                idx_original_temp += stride * nelts

            vectorize[v_slice, nelts](last_dims)

            return

        for _ in range(shape[position]):
            Self.recursive_iters_slice[
                shape, original_shape, steps, starts, ends, backward_op
            ](
                res,
                t1,
                last_dims,
                position + 1,
                last_position,
                idx_temp,
                idx_original_temp,
            )

            idx_temp += strides[position]
            idx_original_temp += steps[position] * t1_strides[position]

    @staticmethod
    fn slice_kernel[
        res_shape: TensorShape,
        original_shape: TensorShape,
        steps: List[Int],
        starts: List[Int],
        ends: List[Int],
        backward_op: Bool = False,
    ](inout res: Tensor[dtype], t1: Tensor[dtype]):
        alias strides = original_shape.strides()

        # Get the dimensions for vectorization
        var last_dims = 1
        var positions_to_skip = 0
        for i in range(res_shape.rank() - 1, -1, -1):
            if steps[i] != 1 and i != res_shape.rank() - 1:
                break
            last_dims *= res_shape[i]
            positions_to_skip += 1
            if starts[i] != 0 or ends[i] != original_shape[i] or steps[i] != 1:
                break

        # Get the dimensions for the first loop
        var first_dims = 1
        var start_position = 0
        for i in range(res_shape.rank() - positions_to_skip):
            if steps[i] != 1 or starts[i] != 0 or ends[i] != original_shape[i]:
                break
            first_dims *= res_shape[i]
            start_position += 1

        var middle_dims = res_shape.num_elements() // last_dims // first_dims

        @parameter
        fn p_slice(i: Int):
            Self.recursive_iters_slice[
                res_shape, original_shape, steps, starts, ends, backward_op
            ](
                res,
                t1,
                last_dims,
                start_position,
                res_shape.rank() - 1 - positions_to_skip,
                i * middle_dims * last_dims,
                i * strides[start_position - 1],
            )

        parallelize[p_slice](first_dims)

    @staticmethod
    fn forward[
        t1_shape: TensorShape,
        attributes: AttributeVector,
    ](inout res: Tensor[dtype], t1: Tensor[dtype]):
        alias axes = attributes["axes"].value().to_shape() if attributes[
            "axes"
        ] else Self.default_axes(t1_shape)
        alias starts = Self.reorder_positions[0](
            attributes["starts"].value().to_shape(), axes, t1_shape
        )
        alias ends = Self.reorder_positions[1](
            attributes["ends"].value().to_shape(), axes, t1_shape
        )
        alias steps = Self.reorder_positions[2](
            attributes["steps"].value().to_shape(), axes, t1_shape
        ) if attributes["steps"] else Self.default_steps(t1_shape)

        alias res_shape = Self.result_shape(t1_shape, attributes)

        Self.slice_kernel[res_shape, t1_shape, steps, starts, ends, False](
            res, t1
        )

    @staticmethod
    fn backward[
        ug_shape: TensorShape,
        t1_shape: TensorShape,
        attributes: AttributeVector = AttributeVector(),
    ](ug: Tensor[dtype], t1: Tensor[dtype]) -> Tensor[dtype]:
        alias axes = attributes["axes"].value().to_shape() if attributes[
            "axes"
        ] else Self.default_axes(t1_shape)
        alias starts = Self.reorder_positions[0](
            attributes["starts"].value().to_shape(), axes, t1_shape
        )
        alias ends = Self.reorder_positions[1](
            attributes["ends"].value().to_shape(), axes, t1_shape
        )
        alias steps = Self.reorder_positions[2](
            attributes["steps"].value().to_shape(), axes, t1_shape
        ) if attributes["steps"] else Self.default_steps(t1_shape)

        var res_grad = Tensor[dtype](t1_shape)

        Self.slice_kernel[ug_shape, t1_shape, steps, starts, ends, True](
            res_grad, ug
        )

        return res_grad^


--- basalt/autograd/ops/ops.mojo ---

from .basics import (
    ADD,
    SUB,
    MUL,
    DIV,
    EXP,
    LOG,
    POW,
    DOT,
    SUM,
    MEAN,
    MAX,
    FLATTEN,
    RESHAPE,
    TRANSPOSE,
    FMA,
)
from .mlops import (
    SIGMOID,
    RELU,
    LEAKYRELU,
    TANH,
    CLIP,
    SQUEEZE,
    UNSQUEEZE,
    SLICE,
)
from .dynamics import CONCAT, SPLIT
from .conv import CONV2D
from .pool import MAXPOOL2D

from basalt import Tensor, TensorShape
from basalt.nn.model import Parameters
from basalt.utils.bytes import Bytes
from basalt.utils.tensorutils import broadcast_shapes, accumulate_grad
from ..attributes import AttributeVector


# Define operators as named parameter expression
@value
@register_passable("trivial")
struct OP(Stringable):
    """
    Compile time Operators list.
    """

    alias ADD = OP(0, "ADD")
    alias SUB = OP(1, "SUB")
    alias MUL = OP(2, "MUL")
    alias DIV = OP(3, "DIV")
    alias EXP = OP(4, "EXP")
    alias LOG = OP(5, "LOG")
    alias POW = OP(6, "POW")
    alias DOT = OP(7, "DOT")
    alias SUM = OP(8, "SUM")
    alias MEAN = OP(9, "MEAN")
    alias MAX = OP(10, "MAX")
    alias FLATTEN = OP(11, "FLATTEN")
    alias RESHAPE = OP(12, "RESHAPE")
    alias SIGMOID = OP(13, "SIGMOID")
    alias RELU = OP(14, "RELU")
    alias TANH = OP(15, "TANH")
    alias CONV2D = OP(16, "CONV2D")
    alias TRANSPOSE = OP(17, "TRANSPOSE")
    alias MAXPOOL2D = OP(18, "MAXPOOL2D")
    alias FMA = OP(19, "FMA")
    alias CLIP = OP(20, "CLIP")
    alias SQUEEZE = OP(21, "SQUEEZE")
    alias UNSQUEEZE = OP(22, "UNSQUEEZE")
    alias CONCAT = OP(23, "CONCAT", dynamic=True)
    alias SPLIT = OP(24, "SPLIT", dynamic=True)
    alias SLICE = OP(25, "SLICE")
    alias LEAKYRELU = OP(28, "LEAKYRELU")

    var id: UInt8
    var name: Bytes[16]
    var dynamic: Bool

    fn __init__(inout self, id: UInt8, name: String, dynamic: Bool = False):
        self.id = id
        self.name = Bytes[16](name)
        self.dynamic = dynamic

    fn __eq__(self, other: OP) -> Bool:
        return self.id == other.id

    fn __str__(self) -> String:
        return str(self.name)


fn static_result_shape(
    op: OP, operands: VariadicList[Symbol], attributes: AttributeVector
) -> TensorShape:
    """
    Static result shape for operators.
    """
    if len(operands) == 1:
        return static_result_shape(op, operands[0].shape, attributes)
    elif len(operands) == 2:
        return static_result_shape(
            op, operands[0].shape, operands[1].shape, attributes
        )
    elif len(operands) == 3:
        return static_result_shape(
            op,
            operands[0].shape,
            operands[1].shape,
            operands[2].shape,
            attributes,
        )
    else:
        print("Error: Invalid number of operands")
        return TensorShape()


fn static_result_shape(
    op: OP, t1_shape: TensorShape, attributes: AttributeVector
) -> TensorShape:
    """
    Static result shape for unary operators.
    """
    if op == OP.EXP:
        return EXP.result_shape(t1_shape)
    elif op == OP.LOG:
        return LOG.result_shape(t1_shape)
    elif op == OP.SUM:
        return SUM.result_shape(t1_shape, attributes)
    elif op == OP.MEAN:
        return MEAN.result_shape(t1_shape, attributes)
    elif op == OP.MAX:
        return MAX.result_shape(t1_shape, attributes)
    elif op == OP.FLATTEN:
        return FLATTEN.result_shape(t1_shape)
    elif op == OP.RESHAPE:
        return RESHAPE.result_shape(t1_shape, attributes)
    elif op == OP.SIGMOID:
        return SIGMOID.result_shape(t1_shape)
    elif op == OP.RELU:
        return RELU.result_shape(t1_shape)
    elif op == OP.LEAKYRELU:
        return LEAKYRELU.result_shape(t1_shape)
    elif op == OP.TANH:
        return TANH.result_shape(t1_shape)
    elif op == OP.TRANSPOSE:
        return TRANSPOSE.result_shape(t1_shape, attributes)
    elif op == OP.MAXPOOL2D:
        return MAXPOOL2D.result_shape(t1_shape, attributes)
    elif op == OP.CLIP:
        return CLIP.result_shape(t1_shape)
    elif op == OP.SQUEEZE:
        return SQUEEZE.result_shape(t1_shape, attributes)
    elif op == OP.UNSQUEEZE:
        return UNSQUEEZE.result_shape(t1_shape, attributes)
    elif op == OP.SLICE:
        return SLICE.result_shape(t1_shape, attributes)
    else:
        print("[ERROR] Operator not found.")
        return TensorShape(-1)


fn static_result_shape(
    op: OP,
    t1_shape: TensorShape,
    t2_shape: TensorShape,
    attributes: AttributeVector,
) -> TensorShape:
    """
    Static result shape for binary operators.
    """
    if op == OP.ADD:
        return ADD.result_shape(t1_shape, t2_shape)
    elif op == OP.SUB:
        return SUB.result_shape(t1_shape, t2_shape)
    elif op == OP.MUL:
        return MUL.result_shape(t1_shape, t2_shape)
    elif op == OP.DIV:
        return DIV.result_shape(t1_shape, t2_shape)
    elif op == OP.POW:
        return POW.result_shape(t1_shape, t2_shape)
    elif op == OP.DOT:
        return DOT.result_shape(t1_shape, t2_shape)
    else:
        # We can't print at compile time (at least for now it crashes at comp time with an error)
        print("[ERROR] Operator not found.")
        return TensorShape(-1, -1)


fn static_result_shape(
    op: OP,
    t1_shape: TensorShape,
    t2_shape: TensorShape,
    t3_shape: TensorShape,
    attributes: AttributeVector,
) -> TensorShape:
    """
    Static result shape for ternary operators.
    """

    if op == OP.CONV2D:
        return CONV2D.result_shape(t1_shape, t2_shape, t3_shape, attributes)
    elif op == OP.FMA:
        return FMA.result_shape(t1_shape, t2_shape, t3_shape)
    else:
        print("[ERROR] Operator not found.")
        return TensorShape(-1, -1)


fn dynamic_result_shape(
    op: OP,
    operands: VariadicList[Symbol],
    attributes: AttributeVector,
) -> List[TensorShape]:
    """
    Static result shape for dynamic operators.
    """
    # Unknown number of inputs and outputs.
    var input_shapes = List[TensorShape]()
    for operand in operands:
        input_shapes.append(operand.shape)

    if op == OP.CONCAT:
        return CONCAT.result_shape(input_shapes, attributes)
    elif op == OP.SPLIT:
        return SPLIT.result_shape(input_shapes, attributes)
    else:
        print("[ERROR] Operator not found.")
        return List[TensorShape](TensorShape(-1))


fn forward_op[
    op: OP, t1_shape: TensorShape, attributes: AttributeVector
](inout res: Tensor[dtype], t1: Tensor[dtype]):
    """
    Forward pass for unary operators.
    """

    @parameter
    if op == OP.EXP:
        EXP.forward[t1_shape](res, t1)
    elif op == OP.LOG:
        LOG.forward[t1_shape](res, t1)
    elif op == OP.SUM:
        SUM.forward[t1_shape, attributes](res, t1)
    elif op == OP.MEAN:
        MEAN.forward[t1_shape, attributes](res, t1)
    elif op == OP.MAX:
        MAX.forward[t1_shape, attributes](res, t1)
    elif op == OP.FLATTEN:
        FLATTEN.forward[t1_shape](res, t1)
    elif op == OP.RESHAPE:
        RESHAPE.forward[t1_shape](res, t1)
    elif op == OP.SIGMOID:
        SIGMOID.forward[t1_shape](res, t1)
    elif op == OP.RELU:
        RELU.forward[t1_shape](res, t1)
    elif op == OP.LEAKYRELU:
        LEAKYRELU.forward[t1_shape, attributes](res, t1)
    elif op == OP.TANH:
        TANH.forward[t1_shape](res, t1)
    elif op == OP.TRANSPOSE:
        TRANSPOSE.forward[t1_shape, attributes](res, t1)
    elif op == OP.MAXPOOL2D:
        MAXPOOL2D.forward[t1_shape, attributes](res, t1)
    elif op == OP.CLIP:
        CLIP.forward[t1_shape, attributes](res, t1)
    elif op == OP.SQUEEZE:
        SQUEEZE.forward[t1_shape, attributes](res, t1)
    elif op == OP.UNSQUEEZE:
        UNSQUEEZE.forward[t1_shape, attributes](res, t1)
    elif op == OP.SLICE:
        SLICE.forward[t1_shape, attributes](res, t1)
    else:
        print("[ERROR] Operator not found.")


fn forward_op[
    op: OP,
    t1_shape: TensorShape,
    t2_shape: TensorShape,
    attributes: AttributeVector,
](inout res: Tensor[dtype], t1: Tensor[dtype], t2: Tensor[dtype]):
    """
    Forward pass for binary operators.
    """

    @parameter
    if op == OP.ADD:
        ADD.forward[t1_shape, t2_shape](res, t1, t2)
    elif op == OP.SUB:
        SUB.forward[t1_shape, t2_shape](res, t1, t2)
    elif op == OP.MUL:
        MUL.forward[t1_shape, t2_shape](res, t1, t2)
    elif op == OP.DIV:
        DIV.forward[t1_shape, t2_shape](res, t1, t2)
    elif op == OP.POW:
        POW.forward[t1_shape, t2_shape](res, t1, t2)
    elif op == OP.DOT:
        DOT.forward[t1_shape, t2_shape](res, t1, t2)
    else:
        print("[ERROR] Operator not found.")


fn forward_op[
    op: OP,
    t1_shape: TensorShape,
    t2_shape: TensorShape,
    t3_shape: TensorShape,
    attributes: AttributeVector,
](
    inout res: Tensor[dtype],
    t1: Tensor[dtype],
    t2: Tensor[dtype],
    t3: Tensor[dtype],
):
    """
    Forward pass for ternary operators.
    """

    @parameter
    if op == OP.CONV2D:
        CONV2D.forward[t1_shape, t2_shape, t3_shape, attributes](
            res, t1, t2, t3
        )
    elif op == OP.FMA:
        FMA.forward[t1_shape, t2_shape, t3_shape](res, t1, t2, t3)
    else:
        print("[ERROR] Operator not found.")


fn forward_op[
    op: OP,
    attributes: AttributeVector,
](
    inputs: List[Symbol],
    outputs: List[Symbol],
    inout parameters: Parameters,
):
    """
    Forward pass for dynamic operators.
    """
    if op == OP.CONCAT:
        CONCAT.forward[attributes](inputs, outputs, parameters)
    elif op == OP.SPLIT:
        SPLIT.forward[attributes](inputs, outputs, parameters)
    else:
        print("[ERROR] Operator not found.")


fn backward_op[
    tensor_id: Int,
    op: OP,
    ug_shape: TensorShape,
    t1_shape: TensorShape,
    attributes: AttributeVector,
](ug: Tensor[dtype], t1: Tensor[dtype], inout grad: Tensor[dtype]):
    """
    Backward pass for unary operators.
    """
    var res_grad: Tensor[dtype]

    @parameter
    if op == OP.EXP:
        res_grad = EXP.backward[ug_shape, t1_shape](ug, t1)
    elif op == OP.LOG:
        res_grad = LOG.backward[ug_shape, t1_shape](ug, t1)
    elif op == OP.SUM:
        res_grad = SUM.backward[ug_shape, t1_shape, attributes](ug, t1)
    elif op == OP.MEAN:
        res_grad = MEAN.backward[ug_shape, t1_shape, attributes](ug, t1)
    elif op == OP.MAX:
        res_grad = MAX.backward[ug_shape, t1_shape, attributes](ug, t1)
    elif op == OP.FLATTEN:
        res_grad = FLATTEN.backward[ug_shape, t1_shape](ug, t1)
    elif op == OP.RESHAPE:
        res_grad = RESHAPE.backward[ug_shape, t1_shape](ug, t1)
    elif op == OP.SIGMOID:
        res_grad = SIGMOID.backward[ug_shape, t1_shape](ug, t1)
    elif op == OP.RELU:
        res_grad = RELU.backward[ug_shape, t1_shape](ug, t1)
    elif op == OP.LEAKYRELU:
        res_grad = LEAKYRELU.backward[ug_shape, t1_shape, attributes](ug, t1)
    elif op == OP.TANH:
        res_grad = TANH.backward[ug_shape, t1_shape](ug, t1)
    elif op == OP.TRANSPOSE:
        res_grad = TRANSPOSE.backward[ug_shape, t1_shape, attributes](ug, t1)
    elif op == OP.MAXPOOL2D:
        res_grad = MAXPOOL2D.backward[ug_shape, t1_shape, attributes](ug, t1)
    elif op == OP.CLIP:
        res_grad = CLIP.backward[ug_shape, t1_shape, attributes](ug, t1)
    elif op == OP.SQUEEZE:
        res_grad = SQUEEZE.backward[ug_shape, t1_shape](ug, t1)
    elif op == OP.UNSQUEEZE:
        res_grad = UNSQUEEZE.backward[ug_shape, t1_shape](ug, t1)
    elif op == OP.SLICE:
        res_grad = SLICE.backward[ug_shape, t1_shape, attributes](ug, t1)
    else:
        print("[ERROR] Operator not found.")
        res_grad = Tensor[dtype](-1)

    accumulate_grad(grad, res_grad)


fn backward_op[
    tensor_id: Int,
    op: OP,
    ug_shape: TensorShape,
    t1_shape: TensorShape,
    t2_shape: TensorShape,
    attributes: AttributeVector,
](
    ug: Tensor[dtype],
    t1: Tensor[dtype],
    t2: Tensor[dtype],
    inout grad: Tensor[dtype],
):
    """
    Backward pass for binary operators.
    """
    var res_grad: Tensor[dtype]

    @parameter
    if op == OP.ADD:
        res_grad = ADD.backward[tensor_id, ug_shape, t1_shape, t2_shape](
            ug, t1, t2
        )
    elif op == OP.SUB:
        res_grad = SUB.backward[tensor_id, ug_shape, t1_shape, t2_shape](
            ug, t1, t2
        )
    elif op == OP.MUL:
        res_grad = MUL.backward[tensor_id, ug_shape, t1_shape, t2_shape](
            ug, t1, t2
        )
    elif op == OP.DIV:
        res_grad = DIV.backward[tensor_id, ug_shape, t1_shape, t2_shape](
            ug, t1, t2
        )
    elif op == OP.POW:
        res_grad = POW.backward[tensor_id, ug_shape, t1_shape, t2_shape](
            ug, t1, t2
        )
    elif op == OP.DOT:
        res_grad = DOT.backward[tensor_id, ug_shape, t1_shape, t2_shape](
            ug, t1, t2
        )
    else:
        print("[ERROR] Operator not found.")
        res_grad = Tensor[dtype](-1, -1)

    fn broadcastable(op: OP) -> Bool:
        return op == OP.ADD or op == OP.SUB or op == OP.MUL or op == OP.DIV

    @parameter
    if broadcastable(op):
        accumulate_grad[
            grad_shape = t1_shape if tensor_id == 0 else t2_shape,
            res_grad_shape = broadcast_shapes(t1_shape, t2_shape),
        ](grad, res_grad)
    else:
        accumulate_grad(grad, res_grad)


fn backward_op[
    tensor_id: Int,
    op: OP,
    ug_shape: TensorShape,
    t1_shape: TensorShape,
    t2_shape: TensorShape,
    t3_shape: TensorShape,
    attributes: AttributeVector,
](
    ug: Tensor[dtype],
    t1: Tensor[dtype],
    t2: Tensor[dtype],
    t3: Tensor[dtype],
    inout grad: Tensor[dtype],
):
    """
    Backward pass for ternary operators.
    """
    var res_grad: Tensor[dtype]

    @parameter
    if op == OP.CONV2D:
        res_grad = CONV2D.backward[
            tensor_id, ug_shape, t1_shape, t2_shape, t3_shape, attributes
        ](ug, t1, t2, t3)
    elif op == OP.FMA:
        res_grad = FMA.backward[
            tensor_id, ug_shape, t1_shape, t2_shape, t3_shape
        ](ug, t1, t2, t3)
    else:
        print("[ERROR] Operator not found.")
        res_grad = Tensor[dtype](-1, -1)

    accumulate_grad(grad, res_grad)


fn backward_op[
    input_id: Int,
    op: OP,
    attributes: AttributeVector,
](
    inputs: List[Symbol],
    outputs: List[Symbol],
    inout grad: Tensor[dtype],
    inout parameters: Parameters,
):
    """
    Backward pass for dynamic operators.
    """
    var res_grad: Tensor[dtype]

    if op == OP.CONCAT:
        res_grad = CONCAT.backward[input_id, attributes](
            inputs, outputs, parameters
        )
    elif op == OP.SPLIT:
        res_grad = SPLIT.backward[input_id, attributes](
            inputs, outputs, parameters
        )
    else:
        print("[ERROR] Operator not found.")
        res_grad = Tensor[dtype](-1, -1)

    accumulate_grad(grad, res_grad)


--- basalt/autograd/ops/pool.mojo ---

from utils.numerics import min_or_neg_inf

from basalt import Tensor, TensorShape
from basalt.autograd.attributes import AttributeVector
from basalt.autograd.ops.conv import get_result_shape


struct MAXPOOL2D:
    @staticmethod
    fn result_shape(
        input_shape: TensorShape, attributes: AttributeVector
    ) -> TensorShape:
        var kernel_size = attributes["kernel_size"].value().to_static[2]()
        var padding = attributes["padding"].value().to_static[2]()
        var stride = attributes["stride"].value().to_static[2]()
        var dilation = attributes["dilation"].value().to_static[2]()

        var res = get_result_shape(
            input_shape,
            TensorShape(kernel_size[0], kernel_size[1]),
            padding,
            stride,
            dilation,
        )

        return TensorShape(input_shape[0], input_shape[1], res[0], res[1])

    @staticmethod
    fn forward[
        input_shape: TensorShape, attributes: AttributeVector
    ](inout outputs: Tensor[dtype], inputs: Tensor[dtype]):
        """
        Returns the max value of each kernel in the input tensor.
            inputs.shape     [batch_size, channels, iX, iY]
            with kernel_size = (kX, kY)
            outputs.shape    [batch_size, channels, oX, oY].
        """
        alias kernel_size = attributes["kernel_size"].value().to_static[2]()
        alias padding = attributes["padding"].value().to_static[2]()
        alias stride = attributes["stride"].value().to_static[2]()
        alias dilation = attributes["dilation"].value().to_static[2]()

        alias inputs_strides = input_shape.strides()
        alias output_shape = Self.result_shape(input_shape, attributes)
        alias outputs_strides = output_shape.strides()

        for batch in range(input_shape[0]):
            for in_ch in range(input_shape[1]):
                for x in range(output_shape[2]):
                    for y in range(output_shape[3]):
                        var max_val: Scalar[dtype] = min_or_neg_inf[dtype]()
                        var ix_base = x * stride[0] - padding[0]
                        var iy_base = y * stride[1] - padding[1]
                        for kx in range(kernel_size[0]):
                            for ky in range(kernel_size[1]):
                                var ix = ix_base + kx * dilation[0]
                                var iy = iy_base + ky * dilation[1]

                                if (
                                    ix < 0
                                    or iy < 0
                                    or ix >= input_shape[2]
                                    or iy >= input_shape[3]
                                ):
                                    continue

                                var idx = (
                                    batch * inputs_strides[0]
                                    + in_ch * inputs_strides[1]
                                    + ix * inputs_strides[2]
                                    + iy
                                )

                                var val = inputs[idx]
                                if val > max_val:
                                    max_val = val

                        var out_idx = (
                            batch * outputs_strides[0]
                            + in_ch * outputs_strides[1]
                            + x * outputs_strides[2]
                            + y
                        )

                        outputs[out_idx] = max_val

    @staticmethod
    fn backward[
        ug_shape: TensorShape, input_shape: TensorShape, attributes: AttributeVector
    ](ug: Tensor[dtype], inputs: Tensor[dtype]) -> Tensor[dtype]:
        """
        Backward operation of MAXPOOL2D.

        Upper gradient of shape: [batch_size, channels, uX, uY]
        """
        alias kernel_size = attributes["kernel_size"].value().to_static[2]()
        alias padding = attributes["padding"].value().to_static[2]()
        alias stride = attributes["stride"].value().to_static[2]()
        alias dilation = attributes["dilation"].value().to_static[2]()

        alias ug_strides = ug_shape.strides()
        alias inputs_strides = input_shape.strides()

        var res = Tensor[dtype](input_shape)

        for batch in range(input_shape[0]):
            for in_ch in range(input_shape[1]):
                for x in range(ug_shape[2]):
                    for y in range(ug_shape[3]):
                        var max_val: Scalar[dtype] = min_or_neg_inf[dtype]()
                        var max_idx: Int = -1
                        var ix_base = x * stride[0] - padding[0]
                        var iy_base = y * stride[1] - padding[1]
                        for kx in range(kernel_size[0]):
                            for ky in range(kernel_size[1]):
                                var ix = ix_base + kx * dilation[0]
                                var iy = iy_base + ky * dilation[1]

                                if (
                                    ix < 0
                                    or iy < 0
                                    or ix >= input_shape[2]
                                    or iy >= input_shape[3]
                                ):
                                    continue

                                var idx = (
                                    batch * inputs_strides[0]
                                    + in_ch * inputs_strides[1]
                                    + ix * inputs_strides[2]
                                    + iy
                                )

                                var val = inputs[idx]
                                if val > max_val:
                                    max_val = val
                                    max_idx = idx

                        var ug_idx = (
                            batch * ug_strides[0]
                            + in_ch * ug_strides[1]
                            + x * ug_strides[2]
                            + y
                        )

                        res[max_idx] += ug[ug_idx]

        return res


--- basalt/autograd/params.mojo ---

from collections.optional import Optional

from basalt import dtype
from basalt import Tensor, TensorShape
from .symbol import Symbol
from .attributes import Attribute


@value
struct Param(CollectionElement, Stringable):
    var data: Optional[List[Scalar[dtype]]]
    var initializer: Optional[Attribute]

    fn __init__(inout self):
        self.data = None
        self.initializer = None

    fn __init__(inout self, data: List[Scalar[dtype]]):
        self.data = data
        self.initializer = None

    fn __init__(inout self, data: Scalar[dtype]):
        self.data = List[Scalar[dtype]](data)
        self.initializer = None

    fn __init__(inout self, initializer: String, *args: Scalar[dtype]):
        # Supported initializers:
        #   "random_uniform", lower_bound, upper_bound
        #   "random_normal", mean, std
        #   #TODO: "kaiming_uniform", mode, nonlinearity
        #   #TODO: "kaiming_normal", mode, nonlinearity
        self.initializer = Attribute("initializer", initializer)
        var data = List[Scalar[dtype]]()
        for arg in args:
            data.append(arg)
        self.data = data

    fn __getitem__(self, i: Int) -> Optional[Scalar[dtype]]:
        if self.data:
            return self.data.value()[][i]
        else:
            return None

    fn __str__(self) -> String:
        var s: String = ""
        if self.data:
            var data = self.data.value()
            s += "["
            for i in range(len(data[])):
                s += str(data[][i])
                if i < len(data[]) - 1:
                    s += ", "
            s += "]"
        return s


@value
struct ParamDict(Sized):
    var symbols: List[Symbol]
    var values: List[Param]

    fn __init__(inout self):
        self.symbols = List[Symbol]()
        self.values = List[Param]()

    fn put(inout self, param_id: Symbol, value: Param = Param()):
        self.symbols.append(param_id)
        self.values.append(value)

    fn get_tensor(self, idx: Int) -> Tensor[dtype]:
        # May only be called at runtime
        var num = self.symbols[idx].shape.num_elements()
        var t = DTypePointer[dtype].alloc(num)
        for i in range(num):
            t[i] = self.values[idx][i].value()[]
        return Tensor[dtype](t, self.symbols[idx].shape)

    fn __len__(self) -> Int:
        return len(self.symbols)


--- basalt/autograd/symbol.mojo ---

from basalt import Tensor, TensorShape


@value
@register_passable("trivial")
struct Symbol(CollectionElement, Stringable, EqualityComparable):
    var name: UInt32
    var dtype: DType
    var shape: TensorShape
    var trainable: Bool

    fn __eq__(self, other: Self) -> Bool:
        return self.name == other.name

    fn __ne__(self, other: Self) -> Bool:
        return self.name != other.name

    fn __str__(self) -> String:
        return self.json()

    fn json(self) -> String:
        return (
            '{"name": "'
            + str(self.name)
            + '", "dtype": "'
            + str(self.dtype)
            + '", "shape": "'
            + str(self.shape)
            + '", "trainable": "'
            + str(self.trainable)
            + '"}'
        )


--- basalt/nn/__init__.mojo ---

from .tensor import Tensor, TensorShape
from .model import Model

from .layers.linear import Linear
from .layers.conv import Conv2d
from .layers.pool import MaxPool2d

from .loss import MSELoss, CrossEntropyLoss
from .activations import (
    Softmax,
    LogSoftmax,
    ReLU,
    LeakyReLU,
    Sigmoid,
    Tanh,
)


--- basalt/nn/activations.mojo ---

from basalt import Tensor, TensorShape
from basalt import Graph, Symbol, OP
from basalt.autograd.attributes import Attribute, AttributeVector


# '''Activation functions.'''
fn ReLU(inout g: Graph, input: Symbol) -> Symbol:
    return g.op(OP.RELU, input)


fn LeakyReLU(
    inout g: Graph, input: Symbol, negative_slope: Scalar[dtype]
) -> Symbol:
    return g.op(
        OP.LEAKYRELU,
        input,
        attributes=AttributeVector(Attribute("negative_slope", negative_slope)),
    )


fn Sigmoid(inout g: Graph, input: Symbol) -> Symbol:
    return g.op(OP.SIGMOID, input)


fn Tanh(inout g: Graph, input: Symbol) -> Symbol:
    return g.op(OP.TANH, input)


fn Softmax(inout g: Graph, input: Symbol, axis: Int) -> Symbol:
    # softmax: exp(x_i) / sum(exp(x_j))
    # stable softmax: exp(x_i - max(x_j)) / sum(exp(x_j - max(x_j)))

    var max_values = g.op(
        OP.MAX, input, attributes=AttributeVector(Attribute("axis", axis))
    )
    var input_minus_max = g.op(OP.SUB, input, max_values)
    var exp_values = g.op(OP.EXP, input_minus_max)
    var sum_values = g.op(
        OP.SUM, exp_values, attributes=AttributeVector(Attribute("axis", axis))
    )

    return g.op(OP.DIV, exp_values, sum_values)


fn LogSoftmax(inout g: Graph, input: Symbol, axis: Int) -> Symbol:
    # stable logsoftmax: log(exp(x_i - max(x_j)) / sum(exp(x_j - max(x_j))))
    # stable logsoftmax: x_i - max(x_j) - log(sum(exp(x_j - max(x_j))))

    var max_values = g.op(
        OP.MAX, input, attributes=AttributeVector(Attribute("axis", axis))
    )
    var input_minus_max = g.op(OP.SUB, input, max_values)
    var exp_values = g.op(OP.EXP, input_minus_max)
    var sum_values = g.op(
        OP.SUM, exp_values, attributes=AttributeVector(Attribute("axis", axis))
    )
    var log_values = g.op(OP.LOG, sum_values)

    return g.op(OP.SUB, input_minus_max, log_values)


--- basalt/nn/initializers.mojo ---

from math import sqrt

from basalt import dtype
from basalt import Tensor, TensorShape
from basalt.utils.rand_utils import rand_normal, rand_uniform


fn initialize_tensor(
    shape: TensorShape, type: String, data: List[Scalar[dtype]]
) -> Tensor[dtype]:
    if type == "random_uniform":
        var low = data[0]
        var high = data[1]
        var t = Tensor[dtype](shape)
        rand_uniform(t, low=low, high=high)
        return t
    elif type == "random_normal":
        var mean = data[0].cast[DType.float64]()
        var std = data[1].cast[DType.float64]()
        var t = Tensor[dtype](shape)
        rand_normal(t, mean=mean, std=std)
        return t
    # elif type == "kaiming_uniform":
    #     # mode, nonlinearity
    #     var mode_id = data[0]
    #     var mode = "fan_in" if mode_id == 0 else "fan_out"
    #     return kaiming_uniform(shape, mode = mode)
    # elif type == "kaiming_normal":
    #     # mode, nonlinearity
    #     var mode_id = data[0]
    #     var mode = "fan_in" if mode_id == 0 else "fan_out"
    #     return kaiming_normal(shape, mode = mode)
    else:
        print("[ERROR] Unsupported initialization type: " + type)
        return Tensor[dtype]()


fn calculate_fan(shape: TensorShape, mode: String) -> Scalar[dtype]:
    """
    Calculate the fan-in and fan-out of any tensor.
    """
    # NOTE: shape.rank() should be > 2
    # mode: "fan_in" or "fan_out"
    if shape.rank() < 2:
        print(
            "[ERROR] Fan in and fan out can not be calculated for tensor with less than"
            " 2 dimensions"
        )

    var num_input_fmaps = shape[1]
    var num_output_fmaps = shape[0]
    var receptive_field_size = 1
    if shape.rank() > 2:
        for i in range(2, shape.rank()):
            receptive_field_size *= shape[i]

    var fan_in = num_input_fmaps * receptive_field_size
    var fan_out = num_output_fmaps * receptive_field_size

    if mode == "fan_in":
        return fan_in
    else:
        return fan_out


# # TODO: https://pytorch.org/docs/stable/_modules/torch/nn/init.html
# fn kaiming_uniform(shape: TensorShape, mode: String = "fan_in", nonlinearity: String = "leaky_relu") -> Tensor[dtype]:
#     var fan = calculate_fan(shape, mode)

#     # TODO: add support for other gains: https://github.com/pytorch/pytorch/blob/main/torch/nn/init.py#L68
#     # Gain for linear and conv layers is 1
#     var gain = 1
#     var std = gain / sqrt(fan)

#     # var bound = sqrt(3) * std.cast[dtype]()
#     var bound = std.cast[dtype]()

#     # print("Shape", shape, "Fan", fan, "Bound", bound)

#     var t = Tensor[dtype](shape)
#     rand_uniform(t, low = -bound, high = bound)
#     return t^


# # TODO: https://pytorch.org/docs/stable/_modules/torch/nn/init.html
# fn kaiming_normal(shape: TensorShape, mode: String = "fan_in", nonlinearity: String = "leaky_relu") -> Tensor[dtype]:
#     var fan = calculate_fan(shape, mode)

#     # TODO: add support for other gains: https://github.com/pytorch/pytorch/blob/main/torch/nn/init.py#L68
#     # Gain for linear and conv layers is 1
#     var gain = 1
#     var std = gain / sqrt(fan)

#     var t = Tensor[dtype](shape)
#     rand_normal(t, mean = 0, std = std.cast[DType.float64]())
#     return t^


--- basalt/nn/layers/__init__.mojo ---



--- basalt/nn/layers/conv.mojo ---

from basalt import Graph, Symbol, OP
from basalt import Tensor, TensorShape
from basalt.utils import q_sqrt
from basalt.autograd.params import Param
from basalt.autograd.attributes import AttributeVector, Attribute


fn Conv2d(
    inout g: Graph,
    inputs: Symbol,
    out_channels: Int,
    kernel_size: StaticIntTuple[2],
    padding: StaticIntTuple[2] = 0,
    stride: StaticIntTuple[2] = 1,
    dilation: StaticIntTuple[2] = 1,
) -> Symbol:
    """
    A 2D Convolution Layer.

    Parameters
        inputs.shape     [batch, in_channels, iX, iY]
        kernel.shape     [out_channels, in_channels, kX, kY] (or weights)
        bias.shape       [out_channels].
        output.shape     [batch, out_channels, oX, oY].
    """

    var in_channels: Int = inputs.shape[1]
    var fan_in: Scalar[dtype] = in_channels * kernel_size[0] * kernel_size[1]
    var bound = q_sqrt(fan_in)
    var weights = g.param(
        TensorShape(out_channels, in_channels, kernel_size[0], kernel_size[1]),
        init=Param("random_uniform", -bound, bound)
        # init=Param("kaiming_uniform", 0)
    )
    var bias = g.param(
        TensorShape(out_channels), init=Param("random_uniform", -bound, bound)
    )

    return g.op(
        OP.CONV2D,
        inputs,
        weights,
        bias,
        attributes=AttributeVector(
            Attribute("padding", padding),
            Attribute("stride", stride),
            Attribute("dilation", dilation),
        ),
    )


--- basalt/nn/layers/dropout.mojo ---

# TODO


--- basalt/nn/layers/linear.mojo ---

from basalt import Tensor, TensorShape
from basalt import Graph, Symbol, OP
from basalt.utils import q_sqrt
from basalt.autograd.params import Param


fn Linear(
    inout g: Graph,
    inputs: Symbol,
    n_outputs: Int,
) -> Symbol:
    """
    A fully connected layer.
    """

    var fan_in: Scalar[dtype] = inputs.shape[1]
    var bound = q_sqrt(fan_in)
    var weights = g.param(
        TensorShape(inputs.shape[1], n_outputs),
        init=Param("random_uniform", -bound, bound)
        # init=Param("random_uniform", 1) # NOTE: mode: fan_out required as weight are defined transposed
    )
    var b = g.param(TensorShape(n_outputs), init=Param("random_uniform", -bound, bound))

    var res = g.op(OP.DOT, inputs, weights)
    return g.op(OP.ADD, res, b)


--- basalt/nn/layers/pool.mojo ---

from basalt import Tensor, TensorShape
from collections.optional import Optional

from basalt import Graph, Symbol, OP
from basalt.autograd.attributes import AttributeVector, Attribute


fn set_static_stride(
    kernel_size: StaticIntTuple[2], stride: Optional[Int] = None
) -> StaticIntTuple[2]:
    if stride:
        return StaticIntTuple[2](stride.value()[], stride.value()[])
    else:
        return kernel_size


fn MaxPool2d(
    inout g: Graph,
    inputs: Symbol,
    kernel_size: StaticIntTuple[2],
    stride: Optional[Int] = None,
    padding: StaticIntTuple[2] = 0,
    dilation: StaticIntTuple[2] = 1,
) -> Symbol:
    """
    A 2D Max Pooling Layer.

    Kernel is unaware of the in_channels and out_channels of the input tensor.
    kernel.size     (kX, kY)
    """

    # TODO: assert padding <= kernel_size / 2 (at compile time)

    var stride_temp = set_static_stride(kernel_size, stride)

    return MaxPool2d(g, inputs, kernel_size, stride_temp, padding, dilation)


fn MaxPool2d(
    inout g: Graph,
    inputs: Symbol,
    kernel_size: StaticIntTuple[2],
    stride: StaticIntTuple[2],  # stride should be 1 or more
    padding: StaticIntTuple[2] = 0,
    dilation: StaticIntTuple[2] = 1,
) -> Symbol:
    """
    A 2D Max Pooling Layer.

    Kernel is unaware of the in_channels and out_channels of the input tensor.
    kernel.size     (kX, kY)
    """
    # TODO: assert padding <= kernel_size / 2 (at compile time)

    return g.op(
        OP.MAXPOOL2D,
        inputs,
        attributes=AttributeVector(
            Attribute("kernel_size", kernel_size),
            Attribute("padding", padding),
            Attribute("stride", stride),
            Attribute("dilation", dilation),
        ),
    )


# # TODO


--- basalt/nn/layers/sequential.mojo ---

# TODO


--- basalt/nn/loss.mojo ---

import basalt.nn as nn
from basalt import Tensor, TensorShape
from basalt import Graph, Symbol, OP


fn MSELoss(
    inout g: Graph,
    y_pred: Symbol,
    y_true: Symbol,
) -> Symbol:
    # 1/N * sum( (outputs - targets)^2 )

    var diff = g.op(OP.SUB, y_true, y_pred)
    var loss = g.op(OP.POW, diff, 2)
    var mean_loss = g.op(OP.MEAN, loss)

    return mean_loss


fn CrossEntropyLoss(
    inout g: Graph,
    y_pred: Symbol,
    y_true: Symbol,
) -> Symbol:
    # -1/N * sum( targets * log_softmax(outputs) )

    var log_softmax = nn.LogSoftmax(g, y_pred, axis=1)

    # CrossEntropy (reduction Mean)
    var targets_log_softmax = g.op(OP.MUL, y_true, log_softmax)
    var ret = g.op(OP.SUM, targets_log_softmax)
    var negDivN = g.op(OP.MUL, ret, -1.0 / y_pred.shape[0])

    return negDivN


--- basalt/nn/model.mojo ---

from collections.optional import Optional, OptionalReg
from pathlib import Path

from sys import env_get_int

from basalt import Graph, Symbol, Tensor, TensorShape
from basalt.autograd.ops import forward_op, backward_op
from basalt.utils.collection import Collection
from basalt.utils.tensorutils import fill
from .initializers import initialize_tensor
from basalt.utils.perf_utils import PerfMetrics
from basalt.utils.onnx_utils import load_onnx_model, export_onnx_model


# When runing mojo -D DEBUG=1 -I . file, a crash happens at some point at runtime because of an error in linking it seems (because of using -I .)
# For now it seems one has to change this variable manually to be able to run model with performance metrics.
alias DEBUG = env_get_int["DEBUG", 0]()


# TODO: remove when ability to concatenate graphs (modules)
fn dv_contains(dv: List[Symbol], symbol: Symbol) -> Bool:
    for i in range(len(dv)):
        if dv[i] == symbol:
            return True
    return False


# TODO: remove when ability to concatenate graphs (modules)
fn n_inference_nodes(g: Graph) -> OptionalReg[Int]:
    """
    Calculate the index of the node up to wich the forward pass should be executed for a model inference.
    When looping in revers: Equals the first index on which the node output is also a graph output.
    The number of inference nodes is that index + 1.
    """
    for i in range(len(g.nodes) - 1, -1, -1):
        for j in range(len(g.nodes[i].outputs)):
            if dv_contains(g.outputs, g.nodes[i].outputs[j]):
                return i + 1
    return None


@value
struct Parameters:
    var tensors: Collection
    var grads: Collection

    fn __init__(inout self):
        self.tensors = Collection()
        self.grads = Collection()


struct Model[
    g: Graph,
    n_inference_nodes: OptionalReg[Int] = n_inference_nodes(g),
]():
    var parameters: Parameters
    var perf_metrics: PerfMetrics

    fn __init__(inout self, inference_only: Bool = False):
        self.parameters = Parameters()

        @parameter
        if DEBUG == 1:
            self.perf_metrics = PerfMetrics(g)
        else:
            self.perf_metrics = PerfMetrics()

        self.allocate_tensor_memory()
        self.allocate_grad_memory()

        # TODO: remove this when ability to concatenate graphs (modules)
        # NOTE: inference_only only used for surpressing the warning.
        if not inference_only and not g.loss_out:
            print("\n\n[WARNING]: No loss defined, model.forward() unavailable!\n\n")
        if not n_inference_nodes:
            print(
                "\n\n[WARNING]: No graph out defined, model.inference()"
                " unavailable!\n\n"
            )

    # TODO: remove when ability to concatenate graphs (modules)
    # Removes the need for splitting in forward and inference mode
    fn forward(inout self, *t_inputs: Tensor[dtype]) -> ref[__lifetime_of(self)] Tensor[dtype]:
        # NOTE: Important detail here is that the order of the inputs must be the same as the order the inputs were defined in the graph.
        # Example: If you were te define the y_true before the x when creating the graph
        #
        #   var g = Graph()
        #   var y_true = g.input(TensorShape(batch_size, n_outputs))
        #   var x = g.input(TensorShape(batch_size, n_inputs))
        #
        # Then the order of the inputs in the forward call must be the same:
        #
        #   model.forward(batch.labels, batch.inputs)

        # 1. Execute a full forward pass (model inference + loss)
        self.execute[g.nodes.size](t_inputs ^)

        # 2. Return loss from allocated output memory
        # TODO: known copy (reference?)
        return self.parameters.tensors[g.loss_out.value()]

    fn inference(inout self, *t_inputs: Tensor[dtype]) -> List[Tensor[dtype]]:
        # 1. Execute forward pass up to model out
        self.execute[n_inference_nodes.value()](t_inputs)

        # 2. Return outputs from allocated output memory
        # TODO: known copies (reference?)
        var outputs = List[Tensor[dtype]]()
        for i in range(len(g.outputs)):
            outputs.append(self.parameters.tensors[g.outputs[i]])
        return outputs ^

    fn execute[num_nodes: Int](inout self, t_input: VariadicListMem[Tensor[dtype]]):
        # 1. Write inputs to allocated input memory
        for i in range(len(g.inputs)):
            self.parameters.tensors[g.inputs[i]] = t_input[i]

        # 2. Loop over all nodes and execute forward operations
        @parameter
        for i in range(num_nodes):
            alias op = g.nodes[i].operator
            alias attrs = g.nodes[i].attributes

            # Save start time for performance metrics
            @parameter
            if DEBUG == 1:
                self.perf_metrics.start_forward_pass()

            @parameter
            if op.dynamic:
                forward_op[op, attrs](
                    g.nodes[i].inputs,
                    g.nodes[i].outputs,
                    self.parameters,
                )
            else:
                # Statically known shapes and number of operands
                alias num_operands = len(g.nodes[i].inputs)
                alias t1 = g.nodes[i].inputs[0]
                alias out = g.nodes[i].outputs[0]

                @parameter
                if num_operands == 1:
                    # Unary operator
                    forward_op[op, t1.shape, attrs](
                        self.parameters.tensors[out], self.parameters.tensors[t1]
                    )
                elif num_operands == 2:
                    # Binary operator
                    alias t2 = g.nodes[i].inputs[1]
                    forward_op[op, t1.shape, t2.shape, attrs](
                        self.parameters.tensors[out],
                        self.parameters.tensors[t1],
                        self.parameters.tensors[t2],
                    )
                elif num_operands == 3:
                    # Ternary operator
                    alias t2 = g.nodes[i].inputs[1]
                    alias t3 = g.nodes[i].inputs[2]
                    forward_op[op, t1.shape, t2.shape, t3.shape, attrs](
                        self.parameters.tensors[out],
                        self.parameters.tensors[t1],
                        self.parameters.tensors[t2],
                        self.parameters.tensors[t3],
                    )

            # Save end time for performance metrics
            @parameter
            if DEBUG == 1:
                self.perf_metrics.end_forward_pass(i)

    fn backward(inout self, *upper_grads: Tensor[dtype]):
        """
        Main entrypoint of backward pass.
        """
        # 1. Initialize output gradient at the beginning of the backward pass
        if len(upper_grads) == 0:
            # TODO remove loss_out tag
            fill(self.parameters.grads[g.loss_out.value()], 1.0)
        else:
            var node_outputs = g.nodes[g.nodes.size - 1].outputs
            if len(upper_grads) != node_outputs.size:
                print(
                    "[WARNING] Number of upper grads does not match number of node"
                    " outputs!"
                )
            for i in range(node_outputs.size):
                self.parameters.grads[node_outputs[i]] = upper_grads[i]

        # 2. Loop over all nodes in reverse order and execute backward operations
        @parameter
        for i in range(g.nodes.size):
            alias reverse_i = g.nodes.size - i - 1
            alias op = g.nodes[reverse_i].operator
            alias attrs = g.nodes[reverse_i].attributes
            alias num_operands = len(g.nodes[reverse_i].inputs)

            # Save start time for performance metrics
            @parameter
            if DEBUG == 1:
                self.perf_metrics.start_backward_pass()

            @parameter
            if op.dynamic:

                @parameter
                for j in range(num_operands):
                    @parameter
                    if g.nodes[reverse_i].inputs[j].trainable:
                        backward_op[j, op, attrs](
                            g.nodes[reverse_i].inputs,
                            g.nodes[reverse_i].outputs,
                            self.parameters.grads[g.nodes[reverse_i].inputs[j]],
                            self.parameters,
                        )
            else:
                # Statically known shapes and number of operands
                alias out = g.nodes[reverse_i].outputs[0]  # or upper_grad symbol
                alias t1 = g.nodes[reverse_i].inputs[0]

                @parameter
                if num_operands == 1:
                    # Unary operator
                    @parameter
                    if t1.trainable:
                        backward_op[0, op, out.shape, t1.shape, attrs](
                            self.parameters.grads[out],
                            self.parameters.tensors[t1],
                            self.parameters.grads[t1],  # grad to be updated: inputs[0]
                        )

                elif num_operands == 2:
                    # Binary operator
                    alias t2 = g.nodes[reverse_i].inputs[1]

                    @parameter
                    if t1.trainable:
                        backward_op[0, op, out.shape, t1.shape, t2.shape, attrs](
                            self.parameters.grads[out],
                            self.parameters.tensors[t1],
                            self.parameters.tensors[t2],
                            self.parameters.grads[t1],  # grad to be updated: inputs[0]
                        )

                    @parameter
                    if t2.trainable:
                        backward_op[1, op, out.shape, t1.shape, t2.shape, attrs](
                            self.parameters.grads[out],
                            self.parameters.tensors[t1],
                            self.parameters.tensors[t2],
                            self.parameters.grads[t2],  # grad to be updated: inputs[1]
                        )

                elif num_operands == 3:
                    # Ternary operator
                    alias t2 = g.nodes[reverse_i].inputs[1]
                    alias t3 = g.nodes[reverse_i].inputs[2]

                    @parameter
                    if t1.trainable:
                        backward_op[
                            0, op, out.shape, t1.shape, t2.shape, t3.shape, attrs
                        ](
                            self.parameters.grads[out],
                            self.parameters.tensors[t1],
                            self.parameters.tensors[t2],
                            self.parameters.tensors[t3],
                            self.parameters.grads[t1],  # grad to be updated: inputs[0]
                        )

                    @parameter
                    if t2.trainable:
                        backward_op[
                            1, op, out.shape, t1.shape, t2.shape, t3.shape, attrs
                        ](
                            self.parameters.grads[out],
                            self.parameters.tensors[t1],
                            self.parameters.tensors[t2],
                            self.parameters.tensors[t3],
                            self.parameters.grads[t2],  # grad to be updated: inputs[1]
                        )

                    @parameter
                    if t3.trainable:
                        backward_op[
                            2, op, out.shape, t1.shape, t2.shape, t3.shape, attrs
                        ](
                            self.parameters.grads[out],
                            self.parameters.tensors[t1],
                            self.parameters.tensors[t2],
                            self.parameters.tensors[t3],
                            self.parameters.grads[t3],  # grad to be updated: inputs[2]
                        )

            # Save end time for performance metrics
            @parameter
            if DEBUG == 1:
                self.perf_metrics.end_backward_pass(i)

    fn allocate_tensor_memory(inout self):
        for i in range(len(g.inputs)):
            self.parameters.tensors.append(
                Tensor[dtype](g.inputs[i].shape), g.inputs[i]
            )

        for i in range(len(g.params)):
            var p = g.params.symbols[i]
            var p_init = g.params.values[i]

            var par: Tensor[dtype]
            if p_init.initializer:
                # 1. Specific parameter initialization defined
                var initializer_attr = p_init.initializer.value()[]
                par = initialize_tensor(
                    shape=p.shape,
                    type=initializer_attr.to_string(),
                    data=p_init.data.value()[],
                )
            elif p_init.data:
                # 2. Parameter initialized with data only
                # Data is assumed to contain the tensor
                par = g.params.get_tensor(i)
            else:
                # Default parameter initialization to zero
                par = Tensor[dtype](p.shape)

            self.parameters.tensors.append(par ^, p)

        for i in range(len(g.nodes)):
            # Assumption: An input or a param cannot be an output of a node
            for j in range(len(g.nodes[i].outputs)):
                self.parameters.tensors.append(
                    Tensor[dtype](g.nodes[i].outputs[j].shape), g.nodes[i].outputs[j]
                )

    fn allocate_grad_memory(inout self):
        # Gradient have same shape as the tensor
        for i in range(len(g.inputs)):
            if g.inputs[i].trainable:
                self.parameters.grads.append(
                    Tensor[dtype](g.inputs[i].shape), g.inputs[i]
                )

        for i in range(len(g.params)):
            var grad = g.params.symbols[i]
            if grad.trainable:
                self.parameters.grads.append(Tensor[dtype](grad.shape), grad)

        for i in range(len(g.nodes)):
            for j in range(len(g.nodes[i].outputs)):
                var out = g.nodes[i].outputs[j]
                if out.trainable:
                    self.parameters.grads.append(Tensor[dtype](out.shape), out)

    fn print_perf_metrics(self, time_format: String = "ns", print_shape: Bool = False):
        self.perf_metrics.print_forward_perf_metrics(time_format, print_shape)
        self.perf_metrics.print_backward_perf_metrics(time_format, print_shape)

    fn load_model_data(inout self, model_path: String):  
        var path = Path(model_path)
        print("Loading model data from:", path)

        try:
            if path.suffix() == ".onnx":
                load_onnx_model(model_path, self.parameters, self.g)
            else:
                print("Model file format not supported:", path.suffix())
        except e:
            print("Error loading model data:", e)

    fn export_model(inout self, model_path: String):
        var path = Path(model_path)
        print("Exporting model to:", path)

        try:
            if path.suffix() == ".onnx":
                export_onnx_model(model_path, self.parameters, self.g)
            else:
                print("Model file format not supported:", path.suffix())
        except e:
            print("Error exporting model:", e)

--- basalt/nn/optim.mojo ---

from math import sqrt
from algorithm import vectorize, parallelize

from .model import Parameters
from basalt import Graph, Tensor, TensorShape
from basalt.utils.collection import Collection
from basalt.utils.math_util import add, sub, mul, div


fn get_trainable_parameters(g: Graph) -> List[Symbol]:
    """
    Get all symbols of trainable parameters.
    """

    var trainable_parameters = List[Symbol]()

    for i in range(len(g.params)):
        if g.params.symbols[i].trainable:
            trainable_parameters.append(g.params.symbols[i])

    return trainable_parameters ^


@value
struct Adam[
    lifetime: MutableLifetime, # using mutability and anylifetime, seems to give problem for now because the the reference can't now for sure if the lifetime is mutable or not
    //,
    g: Graph,
    trainable_parameters: List[Symbol] = get_trainable_parameters(g),
]:
    var parameters: Reference[Parameters, True, lifetime]

    var lr: Scalar[dtype]
    var beta1: Scalar[dtype]
    var beta2: Scalar[dtype]
    var epsilon: Scalar[dtype]
    var iter: Int

    var rms_grads: Collection
    var momentum_grads: Collection

    fn __init__(
        inout self,
        parameters: Reference[Parameters, True, lifetime],
        lr: Scalar[dtype] = 0.001,
        beta1: Scalar[dtype] = 0.9,
        beta2: Scalar[dtype] = 0.999,
        epsilon: Scalar[dtype] = 1e-8,
    ):
        self.parameters = parameters

        self.lr = lr
        self.beta1 = beta1
        self.beta2 = beta2
        self.epsilon = epsilon
        self.iter = 0

        # Capacity of the collections should be the n of trainable parameters
        self.rms_grads = Collection(capacity=len(trainable_parameters))
        self.momentum_grads = Collection(capacity=len(trainable_parameters))

        self.allocate_rms_and_momentum()

    fn zero_grad(inout self):
        """Set all gradients to zero."""
        self.parameters[].grads.set_zero()

    fn step(inout self):
        """Update model parameters."""
        self.iter += 1

        # Loop over all trainable parameters
        @parameter
        fn p_step(i: Int):
            var param = trainable_parameters[i]

            @parameter
            fn v_step[nelts: Int](j: Int):
                var momentum_grads = self.momentum_grads[param].load[nelts](j)
                var rms_grads = self.rms_grads[param].load[nelts](j)
                var grads = self.parameters[].grads[param].load[nelts](j)
                var params = self.parameters[].tensors[param].load[nelts](j)

                # Momentum beta 1
                # f1 = beta1 * momentum + (1 - beta1) * grad
                momentum_grads = self.beta1 * momentum_grads + (1 - self.beta1) * grads
                self.momentum_grads[param].store[nelts](j, momentum_grads)

                # Bias correction
                # f2 = f1 / (1 - beta1 ** iter)
                momentum_grads = momentum_grads / (1 - self.beta1**self.iter)

                # RMS beta 2
                # f1 = beta2 * rms + (1 - beta2) * grad ** 2
                rms_grads = self.beta2 * rms_grads + (1 - self.beta2) * grads * grads
                self.rms_grads[param].store[nelts](j, rms_grads)

                # Bias correction
                # f2 = f1 / (1 - beta2 ** iter)
                rms_grads = rms_grads / (1 - self.beta2**self.iter)

                # tensor = tensor - lr * (f2 / (sqrt(rms) + epsilon))
                params = params - self.lr * (
                    momentum_grads / (sqrt(rms_grads) + self.epsilon)
                )
                self.parameters[].tensors[param].store[nelts](j, params)

            vectorize[v_step, 1](param.shape.num_elements())

        parallelize[p_step](len(trainable_parameters))

    fn allocate_rms_and_momentum(inout self):
        # They are initialized to zero
        # Loop over all trainable parameters
        for i in range(len(trainable_parameters)):
            var param = trainable_parameters[i]
            self.rms_grads.append(Tensor[dtype](param.shape), param)
            self.momentum_grads.append(Tensor[dtype](param.shape), param)


--- basalt/nn/tensor.mojo ---

from testing import assert_true
from algorithm import vectorize

from tensor import Tensor as _Tensor
from tensor import TensorShape as _TensorShape


alias MAX_RANK = 8


@register_passable("trivial")
struct TensorShape(Stringable):
    var _rank: Int
    var _shape: StaticIntTuple[MAX_RANK]

    fn __init__(inout self, *shape: Int):
        self._rank = len(shape)
        self._shape = StaticIntTuple[MAX_RANK]()
        for i in range(min(self._rank, MAX_RANK)):
            self._shape[i] = shape[i]

    fn __init__(inout self, shapes: VariadicList[Int]):
        self._rank = len(shapes)
        self._shape = StaticIntTuple[MAX_RANK]()
        for i in range(min(self._rank, MAX_RANK)):
            self._shape[i] = shapes[i]

    fn __init__(inout self, shape: List[Int]):
        self._rank = len(shape)
        self._shape = StaticIntTuple[MAX_RANK]()
        for i in range(min(self._rank, MAX_RANK)):
            self._shape[i] = shape[i]

    fn __init__[num: Int](inout self, shape: StaticIntTuple[num]):
        self._rank = num
        self._shape = StaticIntTuple[MAX_RANK]()
        for i in range(min(self._rank, MAX_RANK)):
            self._shape[i] = shape[i]

    fn __init__(inout self, rank: Int, shape: StaticIntTuple[MAX_RANK]):
        self._rank = rank
        self._shape = shape

    fn __init__(inout self, owned shape: _TensorShape):
        self._rank = shape.rank()
        self._shape = StaticIntTuple[MAX_RANK]()
        for i in range(min(self._rank, MAX_RANK)):
            self._shape[i] = shape[i]

    @always_inline("nodebug")
    fn __getitem__(self, index: Int) -> Int:
        return self._shape[index if index >= 0 else self._rank + index]

    @always_inline("nodebug")
    fn __setitem__(inout self, index: Int, value: Int):
        self._shape[index if index >= 0 else self._rank + index] = value

    @always_inline("nodebug")
    fn rank(self) -> Int:
        return self._rank

    @always_inline("nodebug")
    fn num_elements(self) -> Int:
        var result = 1
        for i in range(self._rank):
            result *= self._shape[i]
        return result

    @always_inline("nodebug")
    fn strides(self) -> StaticIntTuple[MAX_RANK]:
        var result = StaticIntTuple[MAX_RANK](0)
        result[self._rank - 1] = 1
        for i in range(self._rank - 2, -1, -1):
            result[i] = result[i + 1] * self._shape[i + 1]
        return result

    @always_inline("nodebug")
    fn _std_shape(self) -> _TensorShape:
        var s = List[Int](capacity=self.rank())
        for i in range(self.rank()):
            s.append(self[i])
        return _TensorShape(s)

    @always_inline("nodebug")
    fn __str__(self) -> String:
        return str(self._std_shape())

    @always_inline("nodebug")
    fn __eq__(self, other: TensorShape) -> Bool:
        if self.rank() != other.rank():
            return False
        for i in range(self.rank()):
            if self[i] != other[i]:
                return False
        return True

    @always_inline("nodebug")
    fn __ne__(self, other: TensorShape) -> Bool:
        return not self.__eq__(other)

    @always_inline("nodebug")
    fn __contains__(self, value: Int) -> Bool:
        for i in range(self.rank()):
            if self[i] == value:
                return True
        return False


struct Tensor[dtype: DType](Stringable, Movable, CollectionElement):
    var _data: DTypePointer[dtype]
    var _shape: TensorShape

    fn __init__(inout self, *dims: Int):
        self._shape = TensorShape(dims)
        self._data = DTypePointer[dtype].alloc(self._shape.num_elements())
        memset_zero(self._data, self._shape.num_elements())

    fn __init__(inout self, owned shape: TensorShape):
        self._data = DTypePointer[dtype].alloc(shape.num_elements())
        memset_zero(self._data, shape.num_elements())
        self._shape = shape

    fn __init__(
        inout self, owned data: DTypePointer[dtype], owned shape: TensorShape
    ):
        # NOTE: Remember to use _ = your_tensor that you passed, so there is no weird behavior in this function
        self._data = DTypePointer[dtype].alloc(shape.num_elements())
        self._shape = shape
        
        memcpy(self._data, data, self._shape.num_elements())
        _ = data

    fn __init__(inout self, owned tensor: _Tensor[dtype]):
        self._data = DTypePointer[dtype].alloc(tensor.num_elements())
        self._shape = tensor.shape()

        memcpy(self._data, tensor.unsafe_ptr(), self._shape.num_elements())
        _ = tensor

    fn __moveinit__(inout self, owned other: Tensor[dtype]):
        self._data = other._data
        self._shape = other._shape

    fn __copyinit__(inout self, other: Tensor[dtype]):
        # print("[WARNING] Copying tensor")
        self._data = DTypePointer[dtype].alloc(other._shape.num_elements())
        memcpy(self._data, other._data, other.num_elements())
        self._shape = other._shape

    @always_inline("nodebug")
    fn __getitem__(self, index: Int) -> Scalar[dtype]:
        return self._data[index]

    @always_inline("nodebug")
    fn __setitem__(self, index: Int, value: Scalar[dtype]):
        self._data[index] = value

    @always_inline("nodebug")
    fn data(self) -> DTypePointer[dtype]:
        return self._data

    @always_inline("nodebug")
    fn shape(self) -> TensorShape:
        return self._shape

    @always_inline("nodebug")
    fn load[simd_width: Int](self, index: Int) -> SIMD[dtype, simd_width]:
        return self._data.load[width=simd_width](index)

    @always_inline("nodebug")
    fn store[simd_width: Int](self, index: Int, value: SIMD[dtype, simd_width]):
        self._data.store[width=simd_width](index, value)

    @always_inline("nodebug")
    fn strides(self) -> StaticIntTuple[MAX_RANK]:
        return self._shape.strides()

    @always_inline("nodebug")
    fn rank(self) -> Int:
        return self._shape.rank()

    @always_inline("nodebug")
    fn num_elements(self) -> Int:
        return self._shape.num_elements()

    @always_inline("nodebug")
    fn dim(self, index: Int) -> Int:
        return self._shape[index]

    @always_inline("nodebug")
    fn zero(self):
        memset_zero(self._data, self.num_elements())

    @always_inline("nodebug")
    fn ireshape(inout self, new_shape: TensorShape) raises:
        # NOTE Consider not raising on error
        assert_true(self.num_elements() == new_shape.num_elements())
        self._shape = new_shape

    @always_inline("nodebug")
    fn __str__(self) -> String:
        var new_data = DTypePointer[dtype].alloc(self.num_elements())
        var std_shape = self._shape._std_shape()
        memcpy(new_data, self._data, self.num_elements())
        return str(_Tensor[dtype](ptr=new_data, shape=std_shape))

    @always_inline("nodebug")
    fn __del__(owned self):
        self._data.free()


--- basalt/utils/__init__.mojo ---

from memory.unsafe import bitcast


@always_inline("nodebug")
fn q_sqrt(value: Float32) -> Float32:
    var y = bitcast[DType.float32](0x5F3759DF - (bitcast[DType.uint32](value) >> 1))
    return -y * ((0.5 * value * y).fma(y, -1.5))


--- basalt/utils/bytes.mojo ---

from math import nan
from utils.numerics import inf
from utils.static_tuple import StaticTuple

alias ScalarBytes = DType.uint64.sizeof()


@register_passable("trivial")
struct Bytes[capacity: Int](Stringable, CollectionElement, EqualityComparable):
    """
    Static sequence of bytes.
    """

    var data: StaticTuple[UInt8, capacity]

    fn __init__(inout self):
        var data = StaticTuple[UInt8, capacity](0)

        for i in range(capacity):
            data[i] = 0

        self.data = data

    fn __init__(inout self, s: String):
        var data = StaticTuple[UInt8, capacity](0)
        var length = len(s)

        for i in range(capacity):
            data[i] = ord(s[i]) if i < length else 0

        self.data = data

    @always_inline("nodebug")
    fn __len__(self) -> Int:
        return capacity

    @always_inline("nodebug")
    fn __setitem__(inout self, index: Int, value: UInt8):
        self.data[index] = value

    @always_inline("nodebug")
    fn __getitem__(self, index: Int) -> UInt8:
        return self.data[index]

    @always_inline("nodebug")
    fn __eq__(self, other: Self) -> Bool:
        for i in range(capacity):
            if self[i] != other[i]:
                return False
        return True

    @always_inline("nodebug")
    fn __ne__(self, other: Self) -> Bool:
        for i in range(capacity):
            if self[i] != other[i]:
                return True
        return False

    @always_inline("nodebug")
    fn __str__(self) -> String:
        var result: String = ""

        for i in range(capacity):
            var val = self[i]
            if val != 0:
                result += chr(int(val))

        return result


fn scalar_to_bytes[
    dtype: DType, Size: Int = ScalarBytes
](value: Scalar[dtype]) -> Bytes[Size]:
    constrained[Size >= ScalarBytes, "Size must be at least ${ScalarBytes}"]()

    var bits = bitcast[DType.uint64](value.cast[expand_type[dtype]()]())
    var data = Bytes[Size]()

    for i in range(ScalarBytes):
        data[i] = (bits >> (i << 3)).cast[DType.uint8]()

    return data


fn bytes_to_scalar[dtype: DType](data: Bytes) -> Scalar[dtype]:
    constrained[data.capacity >= ScalarBytes, "Size must be at least ${ScalarBytes}"]()

    var bits: UInt64 = 0

    for i in range(ScalarBytes):
        bits |= data[i].cast[DType.uint64]() << (i << 3)

    return bitcast[expand_type[dtype]()](bits).cast[dtype]()


fn expand_type[dtype: DType]() -> DType:
    @parameter
    if dtype.is_floating_point():
        return DType.float64
    elif dtype.is_signed():
        return DType.int64
    elif dtype.is_integral():
        return DType.uint64
    
    constrained[False, "Type must be numeric"]()
    return DType.invalid


--- basalt/utils/collection.mojo ---

from memory.unsafe_pointer import UnsafePointer, initialize_pointee_move, destroy_pointee

from basalt import Tensor, Symbol


struct Collection(CollectionElement, Sized):
    """
    A collection of tensors with associated symbols.
    """

    var size: Int
    var capacity: Int
    var data: UnsafePointer[Tensor[dtype]]
    var symbols: DTypePointer[DType.uint32]

    @always_inline("nodebug")
    fn __init__(inout self, *, capacity: Int = 0):
        """
        Initializes a new Collection with the given capacity.
        """
        self.size = 0
        self.capacity = capacity
        self.data = UnsafePointer[Tensor[dtype]].alloc(capacity)
        self.symbols = DTypePointer[DType.uint32].alloc(capacity)

    @always_inline("nodebug")
    fn __moveinit__(inout self, owned existing: Self):
        """
        Move initializes a Collection from an existing one.
        """
        self.size = existing.size
        self.capacity = existing.capacity
        self.data = existing.data
        self.symbols = existing.symbols

    @always_inline("nodebug")
    fn __copyinit__(inout self, existing: Self):
        """
        Copy initializes a Collection from an existing one.
        """
        self.capacity = existing.capacity
        self.size = existing.size
        self.data = UnsafePointer[Tensor[dtype]].alloc(existing.capacity)
        self.symbols = DTypePointer[DType.uint32].alloc(existing.capacity)
        memcpy(self.symbols, existing.symbols, existing.capacity)

        for i in range(existing.size):
            initialize_pointee_move((self.data + i), (existing.data + i)[])

    @always_inline("nodebug")
    fn __del__(owned self):
        """
        Destructor for the Collection.
        """
        for i in range(self.size):
            destroy_pointee((self.data + i))
        if self.data:
            self.data.free()
        if self.symbols:
            self.symbols.free()

    @always_inline("nodebug")
    fn __len__(self) -> Int:
        """
        Returns the number of elements in the Collection.
        """
        return self.size

    @always_inline("nodebug")
    fn _realloc(inout self, new_capacity: Int):
        """
        Reallocates the Collection to the new capacity.
        """
        var new_data = UnsafePointer[Tensor[dtype]].alloc(new_capacity)
        var new_symbols = DTypePointer[DType.uint32].alloc(new_capacity)

        for i in range(self.size):
            initialize_pointee_move((new_data + i), (self.data + i)[])
            new_symbols[i] = self.symbols[i]

        self.data.free()
        self.symbols.free()

        self.data = new_data
        self.symbols = new_symbols
        self.capacity = new_capacity

    @always_inline("nodebug")
    fn append(inout self, owned value: Tensor[dtype], symbol: Symbol):
        """
        Appends a tensor and its associated symbol to the Collection.
        """
        self.append(value ^, symbol.name)

    @always_inline("nodebug")
    fn append(inout self, owned value: Tensor[dtype], symbol_name: UInt32):
        """
        Appends a tensor and its associated symbol name to the Collection.
        """
        if self.size >= self.capacity:
            self._realloc(max(1, self.capacity * 2))
        initialize_pointee_move((self.data + self.size), value ^)
        self.symbols[self.size] = symbol_name
        self.size += 1

    @always_inline("nodebug")
    fn get_index(self, symbol_name: UInt32) -> Int:
        """
        Returns the index of the tensor with the given symbol name.
        """        
        alias factor = 8
        # 2 -> 5.32s MNIST
        # 4 -> 4.95s MNIST
        # 8 -> 4.85s MNIST
        # 16 -> 5.19s MNIST
        # NOTE: This ideally should just be a hashmap

        for i in range(0, self.size, factor):
            var elems = self.symbols.load[width=factor](i) == symbol_name

            for j in range(factor):
                if elems[j]: 
                    return i + j

        var split = divmod(self.size, factor)

        for i in range(split[1]):
            var index = split[0] + i
            
            if self.symbols[index] == symbol_name:
                return index

        return -1

    fn __getitem__(
        inout self,
        symbol: Symbol,
    ) -> ref[__lifetime_of(self)] Tensor[dtype]:
        """
        Returns a reference to the tensor with the given symbol.
        """
        var index = self.get_index(symbol.name)


        return (self.data + index)[0]

    @always_inline("nodebug")
    fn clear(inout self):
        """
        Clears the Collection, removing all tensors and symbols.
        """
        for i in range(self.size):
            destroy_pointee((self.data + i))
        memset_zero(self.symbols, self.capacity)
        self.size = 0

    @always_inline("nodebug")
    fn set_zero(self):
        """
        Zeroes out all the tensors in the collection.
        """
        for i in range(self.size):
            self.data[i].zero()


--- basalt/utils/dataloader.mojo ---

from testing import assert_equal
from math import min
from memory import memcpy

from basalt import dtype, nelts
from basalt import Tensor, TensorShape


@value
struct Batch[dtype: DType](CollectionElement):
    var data: Tensor[dtype]
    var labels: Tensor[dtype]

    fn __init__(inout self, batch_data: Tensor[dtype], batch_labels: Tensor[dtype]):
        self.data = batch_data
        self.labels = batch_labels

    fn __init__(
        inout self,
        df_data: Tensor[dtype],
        df_labels: Tensor[dtype],
        start: Int,
        batch_data_shape: TensorShape,
        batch_labels_shape: TensorShape,
    ):
        # TODO: find a better way to do this
        # Links to the copies of the input tensors in model.forward()
        self.data = Tensor[dtype](batch_data_shape)
        self.labels = Tensor[dtype](batch_labels_shape)
        memcpy(
            self.data.data(),
            df_data.data().offset(start * batch_data_shape.strides()[0]),
            batch_data_shape.num_elements(),
        )
        memcpy(
            self.labels.data(),
            df_labels.data().offset(start * batch_labels_shape.strides()[0]),
            batch_labels_shape.num_elements(),
        )

    fn __getitem__(self, index: Int) -> Tensor[dtype]:
        if index == 0:
            return self.data
        elif index == 1:
            return self.labels
        else:
            print("[ERROR] Batch.__getitem__(): Index out of bounds")
            return Tensor[dtype]()


@value
struct DataLoader:
    var data: Tensor[dtype]
    var labels: Tensor[dtype]
    var batch_size: Int
    var _current_index: Int
    var _num_batches: Int
    var _data_batch_shape: TensorShape
    var _label_batch_shape: TensorShape

    fn __init__(
        inout self,
        data: Tensor[dtype],
        labels: Tensor[dtype],
        batch_size: Int,
    ):
        self.data = data
        self.labels = labels
        self.batch_size = batch_size

        # Number of batches to iter, NOTE: ignore the remainder for now
        # var remainder = 1 if self.data.dim(0) % self.batch_size != 0 else 0
        self._current_index = 0
        self._num_batches = self.data.dim(0) // self.batch_size  # + remainder

        # Batch shapes
        self._data_batch_shape = self.data.shape()
        self._label_batch_shape = self.labels.shape()
        self._data_batch_shape[0] = self.batch_size
        self._label_batch_shape[0] = self.batch_size

    @always_inline
    fn __len__(self) -> Int:
        """
        Returns the number of the batches left in the dataset.
        """
        return self._num_batches

    fn __iter__(self) -> Self:
        # TODO: Starting the iterator requires to return (COPY!) the whole dataloader which containts the whole dataset
        # Does this mean that the whole dataset is copied every epoch ?!
        return self

    fn __next__(inout self) -> Batch[dtype]:
        # NOTE: ignore the remainder for now
        # var end = min(self._current_index + self.batch_size, self.data.dim(0))
        # self._data_shape[0] = end - self._current_index
        # self._label_shape[0] = end - self._current_index

        var temp_current_index = self._current_index
        self._current_index += self.batch_size
        self._num_batches -= 1

        return Batch[dtype](
            self.data,
            self.labels,
            temp_current_index,
            self._data_batch_shape,
            self._label_batch_shape,
        )


--- basalt/utils/datasets.mojo ---

from algorithm import vectorize

from basalt import dtype
from basalt import Tensor, TensorShape
from basalt.utils.tensorutils import elwise_op, tmean, tstd


@always_inline
fn div[dtype: DType, simd_width: Int](a: SIMD[dtype, simd_width], b: Scalar[dtype]) -> SIMD[dtype, simd_width]:
    return a / b


struct BostonHousing:
    alias n_inputs = 13

    var data: Tensor[dtype]
    var labels: Tensor[dtype]

    fn __init__(inout self, file_path: String) raises:
        var s = read_file(file_path)
        # Skip the first and last lines
        # This does assume your last line in the file has a newline at the end
        var list_of_lines = s.split("\n")[1:-1]

        # Length is number of lines
        var N = len(list_of_lines)

        self.data = Tensor[dtype](N, self.n_inputs)  # All columns except the last one
        self.labels = Tensor[dtype](N, 1)  # Only the last column (MEDV)

        var line: List[String] = List[String]()

        # Load data in Tensor
        for item in range(N):
            line = list_of_lines[item].split(",")
            self.labels[item] = cast_string[dtype](line[-1])

            for n in range(self.n_inputs):
                self.data[item * self.n_inputs + n] = cast_string[dtype](line[n])

        # Normalize data
        # TODO: redo when tensorutils tmean2 and tstd2 are implemented
        alias nelts = simdwidthof[dtype]()
        var col = Tensor[dtype](N)
        for j in range(self.n_inputs):
            for k in range(N):
                col[k] = self.data[k * self.n_inputs + j]
            for i in range(N):
                self.data[i * self.n_inputs + j] = (self.data[i * self.n_inputs + j] - tmean(col)) / tstd(col)


struct MNIST:
    var data: Tensor[dtype]
    var labels: Tensor[dtype]

    fn __init__(inout self, file_path: String) raises:
        var s = read_file(file_path)
        # Skip the first and last lines
        # This does assume your last line in the file has a newline at the end
        var list_of_lines = s.split("\n")[1:-1]

        # Length is number of lines
        var N = len(list_of_lines)
        self.data = Tensor[dtype](N, 1, 28, 28)
        self.labels = Tensor[dtype](N)

        var line: List[String] = List[String]()

        # Load data in Tensor
        for item in range(N):
            line = list_of_lines[item].split(",")
            self.labels[item] = atol(line[0])
            for i in range(self.data.shape()[2]):
                for j in range(self.data.shape()[3]):
                    self.data[item * 28 * 28 + i * 28 + j] = atol(line[i * 28 + j + 1])

        # Normalize data
        alias nelts = simdwidthof[dtype]()

        @parameter
        fn vecdiv[nelts: Int](idx: Int):
            self.data.store[nelts](idx, div(self.data.load[nelts](idx), 255.0))

        vectorize[vecdiv, nelts](self.data.num_elements())


fn read_file(file_path: String) raises -> String:
    var s: String
    with open(file_path, "r") as f:
        s = f.read()
    return s


fn find_first(s: String, delimiter: String) -> Int:
    for i in range(len(s)):
        if s[i] == delimiter:
            return i
    return -1


fn cast_string[dtype: DType](s: String) raises -> Scalar[dtype]:
    """
    Cast a string with decimal to a SIMD vector of dtype.
    """

    var idx = find_first(s, delimiter=".")
    var x: Scalar[dtype] = -1

    if idx == -1:
        # No decimal point
        x = atol(s)
        return x
    else:
        var c_int: Scalar[dtype]
        var c_frac: Scalar[dtype]
        c_int = atol(s[:idx])
        c_frac = atol(s[idx + 1 :])
        x = c_int + c_frac / (10 ** len(s[idx + 1 :]))
        return x


--- basalt/utils/graph_render.py ---

import onnx
from onnx import helper
from onnx import TensorProto
import netron


def get_param_data(param_shape):
    factor = 1
    for dim in param_shape:
        factor *= dim
    return [0] * factor


def create_onnx_graph_from_json(graph, type="node"):
    # Create a list to hold nodes, inputs, outputs, and initializers
    nodes = []
    inputs = []
    outputs = []
    initializers = []
    intermediates = []

    # Process params as initializers (if operator-graph)
    visited = []
    if type == "operator":
        onnx_inputs = graph["inputs"] + graph.get("params", [])
    elif type == "node":
        onnx_inputs = graph["inputs"]

        # Process params as initializers
        for initializer in graph.get("params", []):
            name = initializer["name"]
            dtype = TensorProto.FLOAT  # TODO
            shape = list(map(int, initializer["shape"].split("x")))
            tensor = helper.make_tensor(name, dtype, shape, get_param_data(shape))
            initializers.append(tensor)
            visited.append(name)

    # Process inputs
    for input in onnx_inputs:
        name = input["name"]
        dtype = TensorProto.FLOAT  # TODO
        shape = list(map(int, input["shape"].split("x")))
        inputs.append(helper.make_tensor_value_info(name, dtype, shape))
        visited.append(name)

    # Process outputs
    for output in graph["outputs"]:
        name = output["name"]
        dtype = TensorProto.FLOAT  # TODO
        shape = list(map(int, output["shape"].split("x")))
        outputs.append(helper.make_tensor_value_info(name, dtype, shape))
        visited.append(name)

    # Process nodes
    for node in graph["nodes"]:
        operator = node["operator"]
        onnx_node = helper.make_node(
            operator,
            inputs=[input["name"] for input in node["inputs"]],
            outputs=[output["name"] for output in node["outputs"]],
            name=f"{node['operator']}_node",
        )

        # Process attributes
        for attribute in node["attributes"]:
            attr_type = 0
            if attribute["type"] == "FLOAT":
                attr_type = onnx.AttributeProto.FLOAT
            elif attribute["type"] == "INT":
                attr_type = onnx.AttributeProto.INT
            elif attribute["type"] == "STRING":
                attr_type = onnx.AttributeProto.STRING
            elif attribute["type"] == "FLOATS":
                attr_type = onnx.AttributeProto.FLOATS
            elif attribute["type"] == "INTS":
                attr_type = onnx.AttributeProto.INTS
            else:
                raise ValueError(f"Unsupported attribute type: {attribute['type']}")

            onnx_attribute = helper.make_attribute(
                        attribute["name"], attribute["value"], attr_type=attr_type
                    )
            onnx_node.attribute.append(onnx_attribute)

        nodes.append(onnx_node)

        # Process intermediates
        for output in node["outputs"]:
            if output["name"] not in visited:
                name = output["name"]
                dtype = TensorProto.FLOAT
                shape = list(map(int, output["shape"].split("x")))
                intermediates.append(helper.make_tensor_value_info(name, dtype, shape))
                visited.append(name)

    # Process loss
    if "loss" in graph.keys():
        loss = graph["loss"][0]
        name = loss["name"]
        if name not in visited:
            dtype = TensorProto.FLOAT
            shape = list(map(int, loss["shape"].split("x")))
            outputs.append(helper.make_tensor_value_info(name, dtype, shape))
            visited.append(name)

    # Create the graph
    graph_def = helper.make_graph(
        nodes,
        graph.get("graph_name", "basalt-ONNX"),
        inputs,
        outputs,
        initializer=initializers,
        value_info=intermediates,
    )

    # Create the model
    model_def = helper.make_model(graph_def, producer_name="basalt")

    # Save the model to a file
    onnx.save(model_def, "output_model.onnx")


def netron_render(graph, type="node"):
    assert type in ["node", "operator"]
    create_onnx_graph_from_json(graph, type=type)
    netron.start("output_model.onnx")


--- basalt/utils/math_util.mojo ---

@always_inline
fn add[
    dtype: DType, simd_width: Int
](a: SIMD[dtype, simd_width], b: SIMD[dtype, simd_width]) -> SIMD[
    dtype, simd_width
]:
    return a + b


@always_inline
fn sub[
    dtype: DType, simd_width: Int
](a: SIMD[dtype, simd_width], b: SIMD[dtype, simd_width]) -> SIMD[
    dtype, simd_width
]:
    return a - b


@always_inline
fn mul[
    dtype: DType, simd_width: Int
](a: SIMD[dtype, simd_width], b: SIMD[dtype, simd_width]) -> SIMD[
    dtype, simd_width
]:
    return a * b


@always_inline
fn div[
    dtype: DType, simd_width: Int
](a: SIMD[dtype, simd_width], b: SIMD[dtype, simd_width]) -> SIMD[
    dtype, simd_width
]:
    return a / b


@always_inline
fn round_simd[
    dtype: DType, simd_width: Int
](x: SIMD[dtype, simd_width]) -> SIMD[dtype, simd_width]:
    return round(x)


--- basalt/utils/onnx_utils.mojo ---

from python import Python
from pathlib import Path
from collections import Set

from basalt.nn.model import Parameters
from basalt.nn.tensor import Tensor, TensorShape
from basalt.autograd.attributes import Attribute, AttributeType
from basalt.autograd.ops import OP
from basalt.autograd.graph import Node

from .tensor_creation_utils import to_numpy, copy_np_data

# NOTE: Maybe we could create our own model representation and from there convert to onnx or others (well we already have it in reallity)
# NOTE: Torch doesn't import onnx, need onnx2torch and it doesn't support operators like reshape?


fn make_onnx_attribute(op: OP, attr: Attribute) raises -> PythonObject:
    var onnx = Python.import_module("onnx")
    var onnx_helper = Python.import_module("onnx.helper")

    var attr_name = str(attr.name)
    var attr_value: PythonObject

    if attr.type == AttributeType.FLOAT:
        attr_value = attr.to_scalar[DType.float64]()
    elif attr.type == AttributeType.INT:
        attr_value = attr.to_int()
    elif attr.type == AttributeType.STRING:
        attr_value = attr.to_string()
    elif attr.type == AttributeType.INTS:
        var temp = attr.to_shape()
        var shape = PythonObject([])
        for i in range(temp.rank()):
            shape.append(temp[i])
        attr_value = shape
    else:
        raise Error("Unsupported attribute type")

    if op == OP.CONV2D or op == OP.MAXPOOL2D:
        if attr_name == "dilation":
            attr_name = "dilations"
        elif attr_name == "kernel_size":
            attr_name = "kernel_shape"
        elif attr_name == "stride":
            attr_name = "strides"
        elif attr_name == "padding":
            attr_name = "pads"
        else:
            raise Error("Unsupported attribute name for operator " + str(op))

    if (op == OP.CONV2D or op == OP.MAXPOOL2D) and attr_name == "pads":
        # Special case for pads in conv and maxpool, onnx wants pads to be [x1_begin, x2_begin…x1_end, x2_end,…],
        attr_value.append(attr_value[0])
        attr_value.append(attr_value[1])

    return onnx_helper.make_attribute(attr_name, attr_value)


fn make_onnx_operator_type(op_type: OP) raises -> String:
    if op_type == OP.ADD:
        return "Add"
    elif op_type == OP.SUB:
        return "Sub"
    elif op_type == OP.MUL:
        return "Mul"
    elif op_type == OP.DOT:
        return "MatMul"
    elif op_type == OP.DIV:
        return "Div"
    elif op_type == OP.EXP:
        return "Exp"
    elif op_type == OP.LOG:
        return "Log"
    elif op_type == OP.SUM:
        # Special case, axis isn't an attribute, instead it is an input, because it can be dynamic
        raise Error(str(op_type) + " is not supported right now for conversion to onnx")
        # return "ReduceSum"
    elif op_type == OP.MEAN:
        raise Error(str(op_type) + " is not supported right now for conversion to onnx")
        # return "ReduceMean"
    elif op_type == OP.MAX:
        raise Error(str(op_type) + " is not supported right now for conversion to onnx")
        # return "ReduceMax"
    elif op_type == OP.CONV2D:
        return "Conv"
    elif op_type == OP.MAXPOOL2D:
        return "MaxPool"
    elif op_type == OP.RELU:
        return "Relu"
    elif op_type == OP.TANH:
        return "Tanh"
    elif op_type == OP.SIGMOID:
        return "Sigmoid"
    elif op_type == OP.RESHAPE:
        return "Reshape"
    elif op_type == OP.TRANSPOSE:
        return "Transpose"
    elif op_type == OP.FLATTEN:
        return "Flatten"
    elif op_type == OP.SQUEEZE:
        return "Squeeze"
    elif op_type == OP.UNSQUEEZE:
        return "Unsqueeze"
    elif op_type == OP.CONCAT:
        return "Concat"
    elif op_type == OP.SPLIT:
        return "Split"
    elif op_type == OP.CLIP:
        return "Clip"
    elif op_type == OP.FMA:
        raise Error(str(op_type) + " operator is not supported in onnx")
    else:
        raise Error("Unsupported operator type " + str(op_type))


# --- Loader and exporter ---
fn load_onnx_model(
    model_path: Path, inout model_parameters: Parameters, g: Graph
) raises:
    # Simple onnx data loader where we load the data in order (so we need to have the correct order of the weights and biases in the model. We don't use the names for the loading)
    var onnx = Python.import_module("onnx")
    var onnx_model = onnx.load(str(model_path))

    for i in range(len(onnx_model.graph.initializer)):
        var tensor = onnx_model.graph.initializer[i]

        if (
            tensor.data_type == onnx.TensorProto.FLOAT
            or tensor.data_type == onnx.TensorProto.INT32
            or tensor.data_type == onnx.TensorProto.INT64
        ):
            var data_np = onnx.numpy_helper.to_array(tensor)

            # Get the shape of data onnx
            var temp = List[Int]()
            for j in range(len(data_np.shape)):
                temp.append(int(data_np.shape[j].to_float64()))
            var data_shape = TensorShape(temp)

            # Compare the shape of the data with the shape of the model tensor
            var model_tensor_shape = g.params.symbols[i].shape

            if data_shape != model_tensor_shape:
                # check if the shape is transposed (reversed), we do this comparison because torch can save sove weights transposed (like gemm operator)

                var raise_error_flag = True
                if data_shape.rank() == model_tensor_shape.rank():
                    var count = 0
                    for j in range(model_tensor_shape.rank()):
                        if (
                            data_shape[data_shape.rank() - j - 1]
                            != model_tensor_shape[j]
                        ):
                            break
                        count += 1

                    if count == model_tensor_shape.rank():
                        raise_error_flag = False
                        data_np = data_np.transpose()

                if raise_error_flag:
                    raise Error(
                        "Shape mismatch for tensor "
                        + str(i)
                        + ". Expected shape: "
                        + str(model_tensor_shape)
                        + ", got shape: "
                        + str(data_shape)
                    )

            copy_np_data(model_parameters.tensors[g.params.symbols[i]], data_np)
        else:
            raise Error("Unsupported data type")


fn create_attributes_and_constant_inputs(node: Node, node_number: Int) raises -> (List[PythonObject], List[PythonObject]):
    var onnx = Python.import_module("onnx")
    var np = Python.import_module("numpy")

    var attributes = List[PythonObject]()
    var inputs = List[PythonObject]()

    for i in range(len(node.attributes)):
        var attr = node.attributes[i]

        @parameter
        fn to_np_array(attr: Attribute) raises -> PythonObject:
            if not attr.type == AttributeType.INTS:
                raise Error("Attribute is not a shape")

            var values_np: PythonObject
            if attr.type == AttributeType.INTS:
                var shape = attr.to_shape()
                values_np = PythonObject([])
                for j in range(shape.rank()):
                    values_np.append(shape[j])
            elif attr.type == AttributeType.FLOAT:
                values_np = attr.to_scalar[DType.float64]()
            elif attr.type == AttributeType.INT:
                values_np = attr.to_int()
            else:
                raise Error("Unsupported attribute type")

            var np_array = np.array(values_np, dtype=np.int64)

            return onnx.numpy_helper.from_array(np_array)
        
        # Special cases where attributes are considered as inputs, so we create Constant inputs
        if node.operator == OP.RESHAPE:
            if str(attr.name) == "shape":
                var outputs = PythonObject([])
                outputs.append(str(node.operator) + "_" + str(attr.name) + "_" + str(node_number))
                var temp_node = onnx.helper.make_node(
                    op_type="Constant",
                    inputs=[],
                    outputs=outputs,
                    value=to_np_array(attr),
                )

                inputs.append(temp_node)
        elif node.operator == OP.CLIP:
            if str(attr.name) == "min" or str(attr.name) == "max":
                var outputs = PythonObject([])
                outputs.append(str(node.operator) + "_" + str(attr.name) + "_" + str(node_number))
                var temp_node = onnx.helper.make_node(
                    op_type="Constant",
                    inputs=[],
                    outputs=outputs,
                    value=to_np_array(attr),
                )

                inputs.append(temp_node)
        elif node.operator == OP.SQUEEZE or node.operator == OP.UNSQUEEZE:
            if str(attr.name) == "dims":
                var outputs = PythonObject([])
                outputs.append(str(node.operator) + "_" + str(attr.name) + "_" + str(node_number))
                var temp_node = onnx.helper.make_node(
                    op_type="Constant",
                    inputs=[],
                    outputs=outputs,
                    value=to_np_array(attr),
                )

                inputs.append(temp_node)
        else:
            var attr_value = make_onnx_attribute(node.operator, attr)

            attributes.append(attr_value)

    return (attributes, inputs)


fn export_onnx_model(model_path: Path, inout model_parameters: Parameters, g: Graph) raises:
    # Create onnx model with data and nodes
    var onnx = Python.import_module("onnx")
    var onnx_helper = Python.import_module("onnx.helper")

    var graph = onnx_helper.make_graph(
        nodes=[],
        name="basalt_model",
        inputs=[],
        outputs=[],
        initializer=[],
        value_info=[],
    )

    var visited = Set[String]()

    # Create onnx initializers
    for i in range(len(g.params.symbols)):
        var tensor = g.params.symbols[i]
        var tensor_data = model_parameters.tensors[tensor]
        var tensor_np = to_numpy(tensor_data)

        # Create onnx tensor
        var onnx_tensor_data = onnx_helper.make_tensor(
            name=str(tensor.name),
            data_type=onnx.TensorProto.FLOAT,
            dims=tensor_np.shape,
            vals=tensor_np,
        )

        graph.initializer.append(onnx_tensor_data)

    # Create onnx nodes
    for i in range(len(g.nodes)):
        var node = g.nodes[i]

        var op_type = make_onnx_operator_type(node.operator)
        var inputs = PythonObject([])
        var outputs = PythonObject([])
        var name = str(node.operator) + "_node" + str(i)

        for j in range(len(node.inputs)):
            inputs.append(str(node.inputs[j].name))
        for j in range(len(node.outputs)):
            outputs.append(str(node.outputs[j].name))

            # Process intermediate
            if str(node.outputs[j].name) not in visited:
                visited.add(str(node.outputs[j].name))
                var intermediate_tensor = node.outputs[j]
                var intermediate_shape = intermediate_tensor.shape

                var name = str(intermediate_tensor.name)
                var dtype = onnx.TensorProto.FLOAT  # TODO
                var shape = PythonObject([])
                for j in range(intermediate_shape.rank()):
                    shape.append(intermediate_shape[j])

                # Create onnx tensor information
                var onnx_output = onnx_helper.make_tensor_value_info(name, dtype, shape)
                graph.value_info.append(onnx_output)

        # Process attributes
        var attributes_and_inputs = create_attributes_and_constant_inputs(node, i)
        var attributes = attributes_and_inputs[0]
        var inputs_constant = attributes_and_inputs[1]
        for j in range(len(inputs_constant)):
            inputs.append(inputs_constant[j].output[0])
            graph.node.append(inputs_constant[j])

        # Create onnx node
        var onnx_node = onnx_helper.make_node(
            op_type,
            inputs,
            outputs,
            name,
        )
        for attribute in attributes:
            onnx_node.attribute.append(attribute[])

        graph.node.append(onnx_node)

    # Create onnx inputs
    for i in range(len(g.inputs)):
        var input_tensor = g.inputs[i]
        var input_shape = input_tensor.shape

        var name = str(input_tensor.name)
        var dtype = onnx.TensorProto.FLOAT  # TODO
        var shape = PythonObject([])
        for j in range(input_shape.rank()):
            shape.append(input_shape[j])

        # Create onnx tensor information
        var onnx_input = onnx_helper.make_tensor_value_info(name, dtype, shape)
        graph.input.append(onnx_input)

    # Create onnx outputs
    for i in range(len(g.outputs)):
        var output_tensor = g.outputs[i]
        var output_shape = output_tensor.shape

        var name = str(output_tensor.name)
        var dtype = onnx.TensorProto.FLOAT  # TODO
        var shape = PythonObject([])
        for j in range(output_shape.rank()):
            shape.append(output_shape[j])

        # Create onnx tensor information
        var onnx_output = onnx_helper.make_tensor_value_info(name, dtype, shape)
        graph.output.append(onnx_output)

    # Create onnx model
    var onnx_model = onnx_helper.make_model(graph, producer_name="basalt")

    # Save onnx model
    onnx.checker.check_model(onnx_model)
    onnx.save(onnx_model, str(model_path))


--- basalt/utils/perf_utils.mojo ---

from time import now
from memory import memset

from basalt.autograd.node import Node


@always_inline("nodebug")
fn fit_string[num: Int](s: String) -> String:
    var data = DTypePointer[DType.uint8]().alloc(num + 1)
    var copy_len = min(num, len(s))

    memcpy(data, s.unsafe_uint8_ptr(), copy_len)
    memset(data + copy_len, ord(" "), num - copy_len)
    data[num] = 0

    return String(data, num + 1)


@always_inline("nodebug")
fn truncate_decimals[num: Int](s: String) -> String:
    try:
        var parts = s.split(".")
        var truncated = parts[0]

        if len(parts) > 1:
            var decimal_parts = parts[1].split("e")
            truncated += "." + fit_string[num](decimal_parts[0])

            if len(decimal_parts) > 1:
                truncated += "e" + decimal_parts[1]

        return truncated
    except e:
        print("[WARNING] could not truncate decimals: ", e)
        return s


@value
struct PerfMetricsValues:
    var node: Node
    var ns: Float64


@value
struct PerfMetrics:
    var forward_perf_metrics: List[PerfMetricsValues]
    var backward_perf_metrics: List[PerfMetricsValues]
    var epochs_forward: Int
    var epochs_backward: Int
    var start: Int

    fn __init__(inout self):
        self.forward_perf_metrics = List[PerfMetricsValues]()
        self.backward_perf_metrics = List[PerfMetricsValues]()
        self.epochs_forward = 0
        self.epochs_backward = 0
        self.start = 0

    fn __init__(inout self, graph: Graph):
        self.forward_perf_metrics = List[PerfMetricsValues]()
        self.backward_perf_metrics = List[PerfMetricsValues]()

        self.forward_perf_metrics.reserve(graph.nodes.size)
        self.backward_perf_metrics.reserve(graph.nodes.size)

        for i in range(graph.nodes.size):
            self.forward_perf_metrics.append(PerfMetricsValues(graph.nodes[i], 0.0))
            self.backward_perf_metrics.append(PerfMetricsValues(graph.nodes[i], 0.0))

        self.epochs_forward = 0
        self.epochs_backward = 0
        self.start = 0

    fn start_forward_pass(inout self):
        self.start = now()

    fn end_forward_pass(inout self, pos: Int):
        self.forward_perf_metrics[pos].ns += now() - self.start
        self.epochs_forward += 1

    fn start_backward_pass(inout self):
        self.start = now()

    fn end_backward_pass(inout self, pos: Int):
        self.backward_perf_metrics[pos].ns += now() - self.start
        self.epochs_backward += 1

    fn print_perf_metrics[
        type_part: String
    ](self, time_format: String = "ns", print_shape: Bool = False):
        constrained[type_part == "Forward" or type_part == "Backward", "Only 'Forward' or 'Backward' are accepted types."]()
        
        alias is_forward = type_part == "Forward"

        var metrics = self.forward_perf_metrics if is_forward else self.backward_perf_metrics
        var epochs = self.epochs_forward if is_forward else self.epochs_backward
        var size = len(metrics)
        var total_time: Float64 = 0

        if size == 0:
            return

        if is_forward:
            print("\n\nForward pass performance metrics:")
        else:
            print("\n\nBackward pass performance metrics:")
            
        for i in range(size):
            total_time += metrics[i].ns

        var header = (
            fit_string[5]("Node")
            + "| "
            + fit_string[15]("Operator")
            + "| "
            + fit_string[20]("Time [" + time_format + "]")
            + "| "
            + fit_string[20]("Percentage [%]")
        )

        if print_shape:
            header += "| " + fit_string[70]("Shape\t <out> = OP( <in1>, <in2>, <in3> )")
            
        print(header)

        var header_length = len(header)
        var seperator = DTypePointer[DType.uint8]().alloc(header_length + 1)
        
        memset(seperator, ord("-"), header_length)
        seperator[header_length] = 0
        
        print(String(seperator, len(header) + 1))

        for i in range(size):
            var value = metrics[i]
            var time = value.ns / epochs

            if time_format == "ms":
                time /=  1e6
            elif time_format == "s":
                time /= 1e9

            var percentage = (value.ns / total_time) * 100

            var print_value = (
                fit_string[5](str(i))
                + "| "
                + fit_string[15](str(value.node.operator))
                + "| "
                + fit_string[20](truncate_decimals[4](str(time)))
                + "| "
                + fit_string[20](truncate_decimals[3](str(percentage)) + " %")
                + "| "
            )

            if print_shape:
                var shape_str = fit_string[15]("<" + str(value.node.outputs[0].shape) + ">")

                for j in range(1, len(value.node.outputs)):
                    shape_str += ", " + fit_string[15]("<" + str(value.node.outputs[j].shape) + ">")

                shape_str += fit_string[7](" = OP(") + fit_string[15]("<" + str(value.node.inputs[0].shape) + ">")

                for j in range(1, len(value.node.inputs)):
                    shape_str += ", " + fit_string[15]("<" + str(value.node.inputs[j].shape) + ">")

                shape_str += ")"

                print(print_value, end="")
                print(shape_str)
            else:
                print(print_value)

        if time_format == "ms":
            total_time /=  1e6
        elif time_format == "s":
            total_time /= 1e9

        print(
            "\nTotal average "
            + type_part
            + " time: "
            + str(total_time)
            + " "
            + time_format
        )


    fn print_forward_perf_metrics(self, time_format: String = "ns", print_shape: Bool = False):
        self.print_perf_metrics["Forward"](time_format, print_shape)

    fn print_backward_perf_metrics(self, time_format: String = "ns", print_shape: Bool = False):
        self.print_perf_metrics["Backward"](time_format, print_shape)


--- basalt/utils/rand_utils.mojo ---

from basalt import Tensor
from random import rand, randn
from algorithm import vectorize
from utils.static_tuple import StaticTuple


@always_inline
fn rand_uniform[dtype: DType](inout res: Tensor[dtype], low: Scalar[dtype], high: Scalar[dtype]):
    var scale = high - low

    rand[dtype](res.data(), res.num_elements())

    @parameter
    fn vecscale[nelts: Int](idx: Int):
        res.store[nelts](idx, res.load[nelts](idx).fma(scale, low))

    vectorize[vecscale, nelts](res.num_elements())


@always_inline
fn rand_normal[dtype: DType](inout res: Tensor[dtype], mean: Float64, std: Float64):
    randn[dtype](res.data(), res.num_elements(), mean, std**2)


@register_passable("trivial")
struct MersenneTwister:
    """
    Pseudo-random generator Mersenne Twister (MT19937-32bit).
    """

    alias N: Int = 624
    alias M: Int = 397
    alias MATRIX_A: Int32 = 0x9908B0DF
    alias UPPER_MASK: Int32 = 0x80000000
    alias LOWER_MASK: Int32 = 0x7FFFFFFF
    alias TEMPERING_MASK_B: Int32 = 0x9D2C5680
    alias TEMPERING_MASK_C: Int32 = 0xEFC60000

    var state: StaticTuple[Int32, Self.N]
    var index: Int

    fn __init__(inout self, seed: Int):
        alias W: Int = 32
        alias F: Int32 = 1812433253
        alias D: Int32 = 0xFFFFFFFF

        self.index = Self.N
        self.state = StaticTuple[Int32, Self.N]()
        self.state[0] = seed & D

        for i in range(1, Self.N):
            var prev = self.state[i - 1]
            self.state[i] = (F * (prev ^ (prev >> (W - 2))) + i) & D

    fn next(inout self) -> Int32:
        if self.index >= Self.N:
            for i in range(Self.N):
                var x = (self.state[i] & Self.UPPER_MASK) + (self.state[(i + 1) % Self.N] & Self.LOWER_MASK)
                var xA = x >> 1
                if x % 2 != 0:
                    xA ^= Self.MATRIX_A
                self.state[i] = self.state[(i + Self.M) % Self.N] ^ xA
            self.index = 0

        var y = self.state[self.index]
        y ^= y >> 11
        y ^= (y << 7) & Self.TEMPERING_MASK_B
        y ^= (y << 15) & Self.TEMPERING_MASK_C
        y ^= y >> 18
        self.index += 1

        return y

    fn next_ui8(inout self) -> UInt8:
        return self.next().value & int(0xFF)


--- basalt/utils/tensor_creation_utils.mojo ---

from python import Python

# maybe this functions should be from the Tensor struct (like tensor.to_numpy()) and tensor.__init__(np_array: PythonObject) to create a tensor from a numpy array and tensor.copy_np_data(np_array: PythonObject) to copy the numpy array to the tensor.


fn to_numpy(tensor: Tensor) -> PythonObject:
    try:
        var np = Python.import_module("numpy")

        np.set_printoptions(4)

        var rank = tensor.rank()
        var dims = PythonObject([])
        for i in range(rank):
            dims.append(tensor.dim(i))
        var pyarray: PythonObject = np.empty(dims, dtype=np.float32)

        var pointer = int(pyarray.__array_interface__["data"][0].to_float64())
        var pointer_d = DTypePointer[tensor.dtype](address=pointer)
        memcpy(pointer_d, tensor.data(), tensor.num_elements())

        _ = tensor

        return pyarray^
    except e:
        print("Error in to numpy", e)
        return PythonObject()


fn to_tensor(np_array: PythonObject) raises -> Tensor[dtype]:
    var shape = List[Int]()
    for i in range(np_array.ndim):
        shape.append(int(np_array.shape[i].to_float64()))
    if np_array.ndim == 0:
        # When the numpy array is a scalar, you need or the reshape to a size 1 ndarray or do this, if not the memcpy gets a memory error (Maybe because it is a register value?).
        var tensor = Tensor[dtype](TensorShape(1))
        tensor[0] = np_array.to_float64().cast[dtype]()
        return tensor^

    var tensor = Tensor[dtype](TensorShape(shape))

    var np_array_2: PythonObject
    try:
        var np = Python.import_module("numpy")
        # copy is also necessary for ops like slices to make them contiguous instead of references.
        np_array_2 = np.float32(np_array.copy())
    except e:
        np_array_2 = np_array.copy()
        print("Error in to_tensor", e)

    var pointer = int(np_array_2.__array_interface__["data"][0].to_float64())
    var pointer_d = DTypePointer[tensor.dtype](address=pointer)
    memcpy(tensor.data(), pointer_d, tensor.num_elements())

    _ = np_array_2
    _ = np_array

    return tensor^


fn copy_np_data(inout tensor: Tensor, np_array: PythonObject) raises:
    var np_array_2: PythonObject
    try:
        var np = Python.import_module("numpy")
        # copy is also necessary for ops like slices to make them contiguous instead of references.
        np_array_2 = np.float32(np_array.copy())
    except e:
        np_array_2 = np_array.copy()
        print("Error in to_tensor", e)

    var pointer = int(np_array_2.__array_interface__["data"][0].to_float64())
    var pointer_d = DTypePointer[tensor.dtype](address=pointer)
    memcpy(tensor.data(), pointer_d, tensor.num_elements())

    _ = np_array_2
    _ = np_array
    _ = tensor


--- basalt/utils/tensorutils.mojo ---

from sys.info import num_physical_cores
from algorithm import vectorize, parallelize
from memory import memset_zero, memset, stack_allocation
from math import sqrt
from random import rand
from utils.numerics import min_finite, max_finite

from basalt import Tensor, TensorShape
from basalt.nn.tensor import MAX_RANK
from basalt.utils.math_util import add, sub, mul, div


# ---- Start -----

@always_inline
fn fill[dtype: DType](inout t: Tensor[dtype], val: Scalar[dtype]):
    @parameter
    fn fill_vec[nelts: Int](idx: Int):
        t.store[nelts](idx, t.load[nelts](idx).splat(val))

    vectorize[fill_vec, nelts](t.num_elements())


# ----- Functions to access positions in tensor data -----
@always_inline
fn get_real_index[
    size: Int, strides_shape: StaticIntTuple[size], broadcast_shape: TensorShape
](i: Int) -> Int:
    # broadcast_shape is of same rank as strides_shape (the not broadcasted shape), because of broadcast_calculate_strides
    var index_res = 0
    var linear_index = i

    @parameter
    fn unroll_dims[dim: Int]():
        alias j = size - 1 - dim
        alias stride_value = strides_shape[j]
        alias shape_value = broadcast_shape[j]

        var divmod_index = divmod(linear_index, shape_value)

        index_res += divmod_index[1] * stride_value
        linear_index = divmod_index[0]

    unroll[unroll_dims, size]()

    return index_res


# ----- Broadcast functions -----
@always_inline
fn broadcast_shapes(s1: TensorShape, s2: TensorShape) -> TensorShape:
    var ndim = max(s1.rank(), s2.rank())
    var diff = abs(s1.rank() - s2.rank())

    var big = s1 if s1.rank() > s2.rank() else s2
    var small = s2 if s1.rank() > s2.rank() else s1

    var res = StaticIntTuple[MAX_RANK](-1)

    for i in range(ndim - 1, diff - 1, -1):
        var a = big[i]
        var b = small[i - diff]
        if b == a:
            res[i] = a
        elif a == 1 or b == 1:
            res[i] = a * b
        else:
            print("[ERROR] Shapes " + str(s1) + " and " + str(s2) + " cannot be broadcasted together.")

    for i in range(diff - 1, -1, -1):
        res[i] = big[i]

    return TensorShape(rank=ndim, shape=res)


@always_inline
fn broadcast_shapes(*s: TensorShape) -> TensorShape:
    var result_shape = s[0]

    for i in range(1, len(s)):
        result_shape = broadcast_shapes(result_shape, s[i])

    return result_shape


@always_inline
fn broadcast_calculate_strides[size: Int, shape: TensorShape, broadcast_shape: TensorShape]() -> StaticIntTuple[size]:
    alias shape_rank = shape.rank()
    alias diff = size - shape_rank

    var strides = StaticIntTuple[size](0)

    var stride = 1
    for i in range(shape_rank - 1, -1, -1):
        if shape[i] != 1:
            strides[i + diff] = stride
            stride *= shape[i]

    return strides

# ----- Element-wise unary operations -----
@always_inline
fn elwise_transform[
    func: fn[dtype: DType, nelts: Int] (x: SIMD[dtype, nelts]) -> SIMD[dtype, nelts],
](inout res: Tensor[dtype], t: Tensor[dtype]):
    @parameter
    fn vecmath[nelts: Int](idx: Int):
        res.store[nelts](idx, func[dtype, nelts](t.load[nelts](idx)))

    vectorize[vecmath, nelts](t.num_elements())


# ----- Element-wise binary operations -----
@always_inline
fn elwise_pow(inout res: Tensor[dtype], t: Tensor[dtype], x: Int):
    @parameter
    fn vecpow[nelts: Int](idx: Int):
        res.store[nelts](idx, pow(t.load[nelts](idx), x))

    vectorize[vecpow, nelts](t.num_elements())


@always_inline
fn elwise_op[
    t1_shape: TensorShape,
    t2_shape: TensorShape,
    func: fn[dtype: DType, nelts: Int] (
        x: SIMD[dtype, nelts], y: SIMD[dtype, nelts]
    ) -> SIMD[dtype, nelts],
](inout res: Tensor[dtype], t1: Tensor[dtype], t2: Tensor[dtype]):
    alias broadcast: Bool = (t1_shape != t2_shape)
    alias is_scalar: Bool = (t2_shape == TensorShape(1))

    @parameter
    if t2_shape == TensorShape(1):
        elwise_op[func](res, t1, t2[0])
    elif t1_shape == TensorShape(1):
        elwise_op[func](res, t1[0], t2)
    elif broadcast and not is_scalar:
        alias res_shape = broadcast_shapes(t1_shape, t2_shape)
        broadcast_elwise_op[t1_shape, t2_shape, res_shape, func](res, t1, t2)
    else:
        elwise_op[func](res, t1, t2)


@always_inline
fn elwise_op[
    func: fn[dtype: DType, nelts: Int] (
        x: SIMD[dtype, nelts], y: SIMD[dtype, nelts]
    ) -> SIMD[dtype, nelts],
](inout res: Tensor[dtype], t1: Tensor[dtype], t2: Tensor[dtype]):
    """Element-wise operation on two tensors of equal shape."""

    @parameter
    fn vecmath[nelts: Int](idx: Int):
        res.store[nelts](
            idx, func[dtype, nelts](t1.load[nelts](idx), t2.load[nelts](idx))
        )

    vectorize[vecmath, nelts](t1.num_elements())


@always_inline
fn elwise_op[
    func: fn[dtype: DType, nelts: Int] (
        x: SIMD[dtype, nelts], y: SIMD[dtype, nelts]
    ) -> SIMD[dtype, nelts],
](inout res: Tensor[dtype], t1: Tensor[dtype], a: Scalar[dtype]):
    """Element-wise operation on a tensor and a scalar."""

    @parameter
    fn vecmath[nelts: Int](idx: Int):
        res.store[nelts](idx, func[dtype, nelts](t1.load[nelts](idx), a))

    vectorize[vecmath, nelts](t1.num_elements())


@always_inline
fn elwise_op[
    func: fn[dtype: DType, nelts: Int] (
        x: SIMD[dtype, nelts], y: SIMD[dtype, nelts]
    ) -> SIMD[dtype, nelts],
](inout res: Tensor[dtype], a: Scalar[dtype], t1: Tensor[dtype]):
    """Element-wise operation on a tensor and a scalar."""

    @parameter
    fn vecmath[nelts: Int](idx: Int):
        res.store[nelts](idx, func[dtype, nelts](a, t1.load[nelts](idx)))

    vectorize[vecmath, nelts](t1.num_elements())


fn broadcast_elwise_op[
    t1_shape: TensorShape,
    t2_shape: TensorShape,
    res_shape: TensorShape,
    func: fn[dtype: DType, nelts: Int] (
        x: SIMD[dtype, nelts], y: SIMD[dtype, nelts]
    ) -> SIMD[dtype, nelts],
](inout res: Tensor[dtype], t1: Tensor[dtype], t2: Tensor[dtype]):
    alias size = res_shape.rank()
    alias strides1 = broadcast_calculate_strides[size, t1_shape, res_shape]()
    alias strides2 = broadcast_calculate_strides[size, t2_shape, res_shape]()

    @parameter
    fn vec_op[nelts: Int](i: Int):
        var index1 = get_real_index[size, strides1, res_shape](i)
        var index2 = get_real_index[size, strides2, res_shape](i)

        res.store[nelts](
            i,
            func[dtype, nelts](t1.load[nelts](index1), t2.load[nelts](index2)),
        )

    # TODO: Check how to vectorize this
    vectorize[vec_op, 1](res.num_elements())


@always_inline
fn accumulate_grad(inout grad: Tensor[dtype], res_grad: Tensor[dtype]):
    # Accumulate gradient without checking for broadcasting
    elwise_op[add](grad, grad, res_grad)


@always_inline
fn accumulate_grad[
    grad_shape: TensorShape, res_grad_shape: TensorShape
](inout grad: Tensor[dtype], res_grad: Tensor[dtype]):
    @parameter
    if grad_shape == res_grad_shape:
        elwise_op[add](grad, grad, res_grad)
    elif res_grad_shape == TensorShape(1):
        elwise_op[add](grad, grad, res_grad[0])
    elif grad_shape != res_grad_shape:
        # Backward resulting gradient (res_grad) was formed from an operation that required broadcasting.
        # In order to accumulate res_grad to the gradient, the res_grad tensor needs to be unbroadcasted.
        # The following is equivalent to: Summing along the axes that were expanded during the broadcasting process.
        alias size = res_grad_shape.rank()
        alias strides_grad = broadcast_calculate_strides[
            size, grad_shape, res_grad_shape
        ]()

        @parameter
        fn vec_op[nelts: Int](i: Int):
            var index = get_real_index[size, strides_grad, res_grad_shape](i)
            grad[index] += res_grad.load[nelts](i).reduce_add()

        # TODO: Check how to vectorize this
        vectorize[vec_op, 1](res_grad.num_elements())


# ---- Transform functions -----
@always_inline
fn transpose_2D[t_shape: TensorShape](t: Tensor[dtype]) -> Tensor[dtype]:
    var t_new = Tensor[dtype](t_shape[1], t_shape[0])

    alias stride = t_shape[0]

    @parameter
    fn proc_row(i: Int):
        @parameter
        fn proc_column[nelts: Int](j: Int):
            t_new.data().offset(j * t_shape[0] + i).simd_strided_store[nelts](
                t.load[nelts](i * t_shape[1] + j), stride
            )

        vectorize[proc_column, nelts](t.dim(1))

    parallelize[proc_row](t_shape[0])

    return t_new ^


@always_inline
fn transpose_2D[t_shape: TensorShape](t: DTypePointer[dtype]) -> DTypePointer[dtype]:
    var t_new = DTypePointer[dtype].alloc(t_shape[1] * t_shape[0])

    alias stride = t_shape[0]

    @parameter
    fn proc_row(i: Int):
        @parameter
        fn proc_column[nelts: Int](j: Int):
            t_new.offset(j * t_shape[0] + i).simd_strided_store[nelts](
                t.load[width=nelts](i * t_shape[1] + j), stride
            )

        vectorize[proc_column, nelts](t_shape[1])

    parallelize[proc_row](t_shape[0])

    return t_new


# ----- Reduction functions -----
@always_inline
fn reduce[
    op: fn[type: DType, simd_width: Int] (
        x: SIMD[type, simd_width], y: SIMD[type, simd_width]
    ) -> SIMD[type, simd_width],
    reduce_op: fn[type: DType, simd_width: Int] (x: SIMD[type, simd_width]) -> SIMD[
        type, 1
    ],
](t: Tensor[dtype], starting_value: SIMD[dtype, nelts]) -> Scalar[dtype]:
    var m: SIMD[dtype, nelts] = starting_value

    @parameter
    fn vecreduce[_nelts: Int](idx: Int):
        @parameter
        if _nelts == 1:
            m[0] = op(m[0], t.load[_nelts](idx)[0])
        else:
            m = op(m, t.load[nelts](idx))

    vectorize[vecreduce, nelts](t.num_elements())
    return reduce_op(m)


fn get_reduce_shape(t: TensorShape, axis: Int) -> TensorShape:
    var rank = t.rank()
    var new_shape = StaticIntTuple[MAX_RANK]()
    for i in range(rank):
        if i == axis:
            new_shape[i] = 1
        else:
            new_shape[i] = t[i]
    return TensorShape(rank=rank, shape=new_shape)


@always_inline
fn reduce[
    op: fn[type: DType, simd_width: Int] (
        x: SIMD[type, simd_width], y: SIMD[type, simd_width]
    ) -> SIMD[type, simd_width],
    reduce_op: fn[type: DType, simd_width: Int] (x: SIMD[type, simd_width]) -> SIMD[
        type, 1
    ],
](
    inout res: Tensor[dtype],
    t: Tensor[dtype],
    axis: Int,
    starting_value: SIMD[dtype, nelts],
):
    var strides = t.strides()

    @parameter
    fn parallel_reduce(i: Int):
        var m: SIMD[dtype, nelts] = starting_value

        var index_base = (i % strides[axis]) + (i // strides[axis]) * (
            strides[axis] * t.dim(axis)
        )

        @parameter
        fn axisreduce[_nelts: Int](j: Int):
            var index = index_base + j * strides[axis]
            if _nelts == 1:
                m[0] = op(
                    m[0],
                    t.data().offset(index).simd_strided_load[_nelts](strides[axis])[0],
                )
            else:
                m = op(
                    m, t.data().offset(index).simd_strided_load[nelts](strides[axis])
                )

        vectorize[axisreduce, nelts](t.dim(axis))

        res[i] = reduce_op(m)

    parallelize[parallel_reduce](t.num_elements() // t.dim(axis))

    _ = strides


@always_inline
fn _reduce_sum[
    type: DType, simd_width: Int
](x: SIMD[type, simd_width]) -> Scalar[type]:
    return x.reduce_add()


@always_inline
fn tsum(t: Tensor[dtype]) -> Scalar[dtype]:
    var starting_value = 0
    return reduce[add, _reduce_sum](t, starting_value)


@always_inline
fn tmean(t: Tensor[dtype]) -> Scalar[dtype]:
    return tsum(t) / t.num_elements()


@always_inline
fn tstd(t: Tensor[dtype]) -> Scalar[dtype]:
    var mu: Scalar[dtype] = tmean(t)
    var variance: Scalar[dtype] = 0

    @parameter
    fn vecvar[nelts: Int](idx: Int):
        var diff = t.load[nelts](idx) - mu
        variance += (diff * diff).reduce_add()

    vectorize[vecvar, nelts](t.num_elements())

    return sqrt(variance / t.num_elements())


@always_inline
fn tsum(inout res: Tensor[dtype], t: Tensor[dtype], axis: Int):
    var starting_value = 0
    reduce[add, _reduce_sum](res, t, axis, starting_value)


@always_inline
fn tmean(inout res: Tensor[dtype], t: Tensor[dtype], axis: Int):
    var num_elements_axis: Scalar[dtype] = t.dim(axis)
    tsum(res, t, axis)
    elwise_op[div](res, res, num_elements_axis)


@always_inline
fn tstd(inout res: Tensor[dtype], t: Tensor[dtype], axis: Int):
    var mu = Tensor[dtype](get_reduce_shape(t.shape(), axis))
    tmean(mu, t, axis)

    var num_elements_axis: Scalar[dtype] = t.dim(axis)
    var strides = t.strides()
    var strides_mu = mu.strides()

    @parameter
    fn get_t_index(
        i: Int, j: Int, axis: Int, shape: TensorShape, strides: StaticIntTuple[MAX_RANK]
    ) -> Int:
        var index_res = 0
        for k in range(shape.rank()):
            if k == axis:
                index_res += j * strides[k]
            else:
                index_res += (i % shape[k]) * strides[k]
        return index_res

    @parameter
    fn get_mu_index(
        i: Int, axis: Int, shape: TensorShape, strides: StaticIntTuple[MAX_RANK]
    ) -> Int:
        var index_res = 0
        for k in range(shape.rank()):
            if k != axis:
                index_res += (i % shape[k]) * strides[k]
        return index_res

    for i in range(t.num_elements() // t.dim(axis)):
        var mu_index = get_mu_index(i, axis, mu.shape(), strides_mu)

        @parameter
        fn vecvar[nelts: Int](j: Int):
            var t_index = get_t_index(i, j, axis, t.shape(), strides)
            var diff = t.load[nelts](t_index) - mu[mu_index]
            res[i] += (diff * diff).reduce_add()

        vectorize[vecvar, nelts](t.dim(axis))

        res[i] /= num_elements_axis

    _ = (strides, strides_mu)
    elwise_transform[sqrt](res, res)


@always_inline
fn _reduce_max[
    type: DType, simd_width: Int
](x: SIMD[type, simd_width]) -> Scalar[type]:
    return x.reduce_max()


@always_inline
fn tmax(t: Tensor[dtype]) -> Scalar[dtype]:
    var starting_value = min_finite[dtype]()
    return reduce[max, _reduce_max](t, starting_value)


@always_inline
fn tmax(inout res: Tensor[dtype], t: Tensor[dtype], axis: Int):
    var starting_value = min_finite[dtype]()
    reduce[max, _reduce_max](res, t, axis, starting_value)


# @always_inline
# fn transpose[
#     dtype: DType, nelts: Int
# ](t: Tensor[dtype], dim_0: Int, dim_1: Int) -> Tensor[dtype]:
#     """
#     Create a new tensor transposing dim_0 and dim_1.
#     """
#     var axes = DynamicVector[Int](t.rank())

#     for i in range(t.rank()):
#         if i == dim_0:
#             axes.push_back(dim_1)
#         elif i == dim_1:
#             axes.push_back(dim_0)
#         else:
#             axes.push_back(i)

#     return transpose[dtype, nelts](t, axes)


# @always_inline
# fn transpose(inout res: Tensor[dtype], t: Tensor[dtype]):
#     """
#     Create a new transposed tensor of the given tensor t.
#     """
#     var axes = DynamicVector[Int](capacity=t.rank())

#     for i in range(t.rank() - 1, -1, -1):
#         axes.push_back(i)

#     var axes_shape = TensorShape(axes)

#     transpose(res, t, axes_shape)


# @always_inline
# fn transpose(t: Tensor[dtype], axes: DynamicVector[Int]) -> Tensor[dtype]:
#     var new_shape = DynamicVector[Int](capacity=t.rank())
#     for i in range(t.rank()):
#         new_shape.push_back(t.dim(axes[i]))

#     var t_new_shape = TensorShape(new_shape)
#     var t_new = Tensor[dtype](t_new_shape)

#     transpose(t_new, t, t_new_shape)

#     return t_new


@always_inline
fn get_transpose_shape(t: TensorShape, axes: TensorShape) -> TensorShape:
    var rank = t.rank()
    var new_shape = StaticIntTuple[MAX_RANK]()

    for i in range(rank):
        new_shape[i] = t[axes[i]]

    return TensorShape(rank=rank, shape=new_shape)


@always_inline
fn transpose(t: Tensor[dtype], axes: TensorShape) -> Tensor[dtype]:
    var t_new_shape = get_transpose_shape(t.shape(), axes)
    var t_new = Tensor[dtype](t_new_shape)

    transpose(t_new, t, axes)

    return t_new ^


@always_inline
fn transpose(inout res: Tensor[dtype], t: Tensor[dtype], axes: TensorShape):
    """
    Create a new transposed tensor of the given tensor t.
    """
    # NOTE: The rank of of the t tensor should be 2 or more
    # NOTE: Axes should be the same size as the rank of t

    var original_strides = t.strides()
    var transposed_strides = res.strides()

    var position_of_last_rank_new_shape = 0

    # Get position of where the last dim of the old shape is in the new shape
    for i in range(axes.rank()):
        if t.rank() - 1 == axes[i]:
            position_of_last_rank_new_shape = i

    @parameter
    fn p_transpose(i: Int):
        @parameter
        fn v_transpose[nelts: Int](j: Int):
            var new_index = 0
            var original_index = i * t.dim(t.rank() - 1) + j
            var linear_index = original_index
            for k in range(t.rank()):
                # axes tells us the position of where the dim in the transposed shape is located in the original shape
                var stride = original_strides[axes[k]]
                var index = linear_index // stride % t.dim(axes[k])

                new_index += index * transposed_strides[k]

            res.data().offset(new_index).simd_strided_store[nelts](
                t.load[nelts](original_index),
                transposed_strides[position_of_last_rank_new_shape],
            )

        vectorize[v_transpose, nelts](t.dim(t.rank() - 1))

    parallelize[p_transpose](t.num_elements() // t.dim(t.rank() - 1))

    _ = (original_strides, transposed_strides)


# # NOTE: This function can be used for later for optimziation (Many operations in gpu is preferred to pad the tensors when using conv or matmul operations)
# # TODO: Deprecate this function, as it is not used anymore
# @always_inline
# fn pad_zeros[
#     dtype: DType, nelts: Int
# ](t: Tensor[dtype], pad_with: DynamicVector[Int]) -> Tensor[dtype]:
#     """
#     Pad a tensor with zeros along the specified axes of an N dimensional tensor.
#     Number of values padded to the edges of each axis.
#     Example: ((before_1, after_1), ... (before_N, after_N)).
#     """

#     # NOTE: The rank of of the t tensor should be equal to the size of pad_with devided by 2.
#     # As pad_with contains (before, after) number of paddings for each axis.
#     var new_shape = DynamicVector[Int](t.rank())
#     for i in range(t.rank()):
#         new_shape.push_back(t.dim(i) + pad_with[i * 2] + pad_with[i * 2 + 1])
#     var t_new = Tensor[dtype](new_shape)

#     var original_strides = t.strides()
#     var result_strides = t_new.strides()

#     # Parallelize over the first axis
#     # NOTE: Possible dynamically choose the axis to parallelize over
#     @parameter
#     fn p_pad(i: Int):
#         for j in range(t.num_elements() // t.dim(0)):
#             var original_index = i * original_strides[0] + j

#             # Padding contribution of the first dimention
#             var dest_index = (i + pad_with[0]) * result_strides[0]

#             # Calculate the contribution from each dimension
#             var remaining_index = j % original_strides[0]
#             for dim in range(1, t.rank()):
#                 var stride = original_strides[dim]
#                 var index = remaining_index // stride
#                 remaining_index = remaining_index % stride

#                 dest_index += (index + pad_with[dim * 2]) * result_strides[dim]

#             # TODO: figure out vectorization
#             t_new[dest_index] = t[original_index]

#     parallelize[p_pad](t.dim(0))

#     _ = (original_strides, result_strides)

#     return t_new


--- examples/data/housing.csv ---

CRIM,ZN,INDUS,CHAS,NOX,RM,AGE,DIS,RAD,TAX,PTRATIO,B,LSTAT,MEDV
0.00632,18.0,2.31,0,0.538,6.575,65.2,4.09,1,296.0,15.3,396.9,4.98,24.0
0.02731,0.0,7.07,0,0.469,6.421,78.9,4.9671,2,242.0,17.8,396.9,9.14,21.6
0.02729,0.0,7.07,0,0.469,7.185,61.1,4.9671,2,242.0,17.8,392.83,4.03,34.7
0.03237,0.0,2.18,0,0.458,6.998,45.8,6.0622,3,222.0,18.7,394.63,2.94,33.4
0.06905,0.0,2.18,0,0.458,7.147,54.2,6.0622,3,222.0,18.7,396.9,5.33,36.2
0.02985,0.0,2.18,0,0.458,6.43,58.7,6.0622,3,222.0,18.7,394.12,5.21,28.7
0.08829,12.5,7.87,0,0.524,6.012,66.6,5.5605,5,311.0,15.2,395.6,12.43,22.9
0.14455,12.5,7.87,0,0.524,6.172,96.1,5.9505,5,311.0,15.2,396.9,19.15,27.1
0.21124,12.5,7.87,0,0.524,5.631,100.0,6.0821,5,311.0,15.2,386.63,29.93,16.5
0.17004,12.5,7.87,0,0.524,6.004,85.9,6.5921,5,311.0,15.2,386.71,17.1,18.9
0.22489,12.5,7.87,0,0.524,6.377,94.3,6.3467,5,311.0,15.2,392.52,20.45,15.0
0.11747,12.5,7.87,0,0.524,6.009,82.9,6.2267,5,311.0,15.2,396.9,13.27,18.9
0.09378,12.5,7.87,0,0.524,5.889,39.0,5.4509,5,311.0,15.2,390.5,15.71,21.7
0.62976,0.0,8.14,0,0.538,5.949,61.8,4.7075,4,307.0,21.0,396.9,8.26,20.4
0.63796,0.0,8.14,0,0.538,6.096,84.5,4.4619,4,307.0,21.0,380.02,10.26,18.2
0.62739,0.0,8.14,0,0.538,5.834,56.5,4.4986,4,307.0,21.0,395.62,8.47,19.9
1.05393,0.0,8.14,0,0.538,5.935,29.3,4.4986,4,307.0,21.0,386.85,6.58,23.1
0.7842,0.0,8.14,0,0.538,5.99,81.7,4.2579,4,307.0,21.0,386.75,14.67,17.5
0.80271,0.0,8.14,0,0.538,5.456,36.6,3.7965,4,307.0,21.0,288.99,11.69,20.2
0.7258,0.0,8.14,0,0.538,5.727,69.5,3.7965,4,307.0,21.0,390.95,11.28,18.2
1.25179,0.0,8.14,0,0.538,5.57,98.1,3.7979,4,307.0,21.0,376.57,21.02,13.6
0.85204,0.0,8.14,0,0.538,5.965,89.2,4.0123,4,307.0,21.0,392.53,13.83,19.6
1.23247,0.0,8.14,0,0.538,6.142,91.7,3.9769,4,307.0,21.0,396.9,18.72,15.2
0.98843,0.0,8.14,0,0.538,5.813,100.0,4.0952,4,307.0,21.0,394.54,19.88,14.5
0.75026,0.0,8.14,0,0.538,5.924,94.1,4.3996,4,307.0,21.0,394.33,16.3,15.6
0.84054,0.0,8.14,0,0.538,5.599,85.7,4.4546,4,307.0,21.0,303.42,16.51,13.9
0.67191,0.0,8.14,0,0.538,5.813,90.3,4.682,4,307.0,21.0,376.88,14.81,16.6
0.95577,0.0,8.14,0,0.538,6.047,88.8,4.4534,4,307.0,21.0,306.38,17.28,14.8
0.77299,0.0,8.14,0,0.538,6.495,94.4,4.4547,4,307.0,21.0,387.94,12.8,18.4
1.00245,0.0,8.14,0,0.538,6.674,87.3,4.239,4,307.0,21.0,380.23,11.98,21.0
1.13081,0.0,8.14,0,0.538,5.713,94.1,4.233,4,307.0,21.0,360.17,22.6,12.7
1.35472,0.0,8.14,0,0.538,6.072,100.0,4.175,4,307.0,21.0,376.73,13.04,14.5
1.38799,0.0,8.14,0,0.538,5.95,82.0,3.99,4,307.0,21.0,232.6,27.71,13.2
1.15172,0.0,8.14,0,0.538,5.701,95.0,3.7872,4,307.0,21.0,358.77,18.35,13.1
1.61282,0.0,8.14,0,0.538,6.096,96.9,3.7598,4,307.0,21.0,248.31,20.34,13.5
0.06417,0.0,5.96,0,0.499,5.933,68.2,3.3603,5,279.0,19.2,396.9,9.68,18.9
0.09744,0.0,5.96,0,0.499,5.841,61.4,3.3779,5,279.0,19.2,377.56,11.41,20.0
0.08014,0.0,5.96,0,0.499,5.85,41.5,3.9342,5,279.0,19.2,396.9,8.77,21.0
0.17505,0.0,5.96,0,0.499,5.966,30.2,3.8473,5,279.0,19.2,393.43,10.13,24.7
0.02763,75.0,2.95,0,0.428,6.595,21.8,5.4011,3,252.0,18.3,395.63,4.32,30.8
0.03359,75.0,2.95,0,0.428,7.024,15.8,5.4011,3,252.0,18.3,395.62,1.98,34.9
0.12744,0.0,6.91,0,0.448,6.77,2.9,5.7209,3,233.0,17.9,385.41,4.84,26.6
0.1415,0.0,6.91,0,0.448,6.169,6.6,5.7209,3,233.0,17.9,383.37,5.81,25.3
0.15936,0.0,6.91,0,0.448,6.211,6.5,5.7209,3,233.0,17.9,394.46,7.44,24.7
0.12269,0.0,6.91,0,0.448,6.069,40.0,5.7209,3,233.0,17.9,389.39,9.55,21.2
0.17142,0.0,6.91,0,0.448,5.682,33.8,5.1004,3,233.0,17.9,396.9,10.21,19.3
0.18836,0.0,6.91,0,0.448,5.786,33.3,5.1004,3,233.0,17.9,396.9,14.15,20.0
0.22927,0.0,6.91,0,0.448,6.03,85.5,5.6894,3,233.0,17.9,392.74,18.8,16.6
0.25387,0.0,6.91,0,0.448,5.399,95.3,5.87,3,233.0,17.9,396.9,30.81,14.4
0.21977,0.0,6.91,0,0.448,5.602,62.0,6.0877,3,233.0,17.9,396.9,16.2,19.4
0.08873,21.0,5.64,0,0.439,5.963,45.7,6.8147,4,243.0,16.8,395.56,13.45,19.7
0.04337,21.0,5.64,0,0.439,6.115,63.0,6.8147,4,243.0,16.8,393.97,9.43,20.5
0.0536,21.0,5.64,0,0.439,6.511,21.1,6.8147,4,243.0,16.8,396.9,5.28,25.0
0.04981,21.0,5.64,0,0.439,5.998,21.4,6.8147,4,243.0,16.8,396.9,8.43,23.4
0.0136,75.0,4.0,0,0.41,5.888,47.6,7.3197,3,469.0,21.1,396.9,14.8,18.9
0.01311,90.0,1.22,0,0.403,7.249,21.9,8.6966,5,226.0,17.9,395.93,4.81,35.4
0.02055,85.0,0.74,0,0.41,6.383,35.7,9.1876,2,313.0,17.3,396.9,5.77,24.7
0.01432,100.0,1.32,0,0.411,6.816,40.5,8.3248,5,256.0,15.1,392.9,3.95,31.6
0.15445,25.0,5.13,0,0.453,6.145,29.2,7.8148,8,284.0,19.7,390.68,6.86,23.3
0.10328,25.0,5.13,0,0.453,5.927,47.2,6.932,8,284.0,19.7,396.9,9.22,19.6
0.14932,25.0,5.13,0,0.453,5.741,66.2,7.2254,8,284.0,19.7,395.11,13.15,18.7
0.17171,25.0,5.13,0,0.453,5.966,93.4,6.8185,8,284.0,19.7,378.08,14.44,16.0
0.11027,25.0,5.13,0,0.453,6.456,67.8,7.2255,8,284.0,19.7,396.9,6.73,22.2
0.1265,25.0,5.13,0,0.453,6.762,43.4,7.9809,8,284.0,19.7,395.58,9.5,25.0
0.01951,17.5,1.38,0,0.4161,7.104,59.5,9.2229,3,216.0,18.6,393.24,8.05,33.0
0.03584,80.0,3.37,0,0.398,6.29,17.8,6.6115,4,337.0,16.1,396.9,4.67,23.5
0.04379,80.0,3.37,0,0.398,5.787,31.1,6.6115,4,337.0,16.1,396.9,10.24,19.4
0.05789,12.5,6.07,0,0.409,5.878,21.4,6.498,4,345.0,18.9,396.21,8.1,22.0
0.13554,12.5,6.07,0,0.409,5.594,36.8,6.498,4,345.0,18.9,396.9,13.09,17.4
0.12816,12.5,6.07,0,0.409,5.885,33.0,6.498,4,345.0,18.9,396.9,8.79,20.9
0.08826,0.0,10.81,0,0.413,6.417,6.6,5.2873,4,305.0,19.2,383.73,6.72,24.2
0.15876,0.0,10.81,0,0.413,5.961,17.5,5.2873,4,305.0,19.2,376.94,9.88,21.7
0.09164,0.0,10.81,0,0.413,6.065,7.8,5.2873,4,305.0,19.2,390.91,5.52,22.8
0.19539,0.0,10.81,0,0.413,6.245,6.2,5.2873,4,305.0,19.2,377.17,7.54,23.4
0.07896,0.0,12.83,0,0.437,6.273,6.0,4.2515,5,398.0,18.7,394.92,6.78,24.1
0.09512,0.0,12.83,0,0.437,6.286,45.0,4.5026,5,398.0,18.7,383.23,8.94,21.4
0.10153,0.0,12.83,0,0.437,6.279,74.5,4.0522,5,398.0,18.7,373.66,11.97,20.0
0.08707,0.0,12.83,0,0.437,6.14,45.8,4.0905,5,398.0,18.7,386.96,10.27,20.8
0.05646,0.0,12.83,0,0.437,6.232,53.7,5.0141,5,398.0,18.7,386.4,12.34,21.2
0.08387,0.0,12.83,0,0.437,5.874,36.6,4.5026,5,398.0,18.7,396.06,9.1,20.3
0.04113,25.0,4.86,0,0.426,6.727,33.5,5.4007,4,281.0,19.0,396.9,5.29,28.0
0.04462,25.0,4.86,0,0.426,6.619,70.4,5.4007,4,281.0,19.0,395.63,7.22,23.9
0.03659,25.0,4.86,0,0.426,6.302,32.2,5.4007,4,281.0,19.0,396.9,6.72,24.8
0.03551,25.0,4.86,0,0.426,6.167,46.7,5.4007,4,281.0,19.0,390.64,7.51,22.9
0.05059,0.0,4.49,0,0.449,6.389,48.0,4.7794,3,247.0,18.5,396.9,9.62,23.9
0.05735,0.0,4.49,0,0.449,6.63,56.1,4.4377,3,247.0,18.5,392.3,6.53,26.6
0.05188,0.0,4.49,0,0.449,6.015,45.1,4.4272,3,247.0,18.5,395.99,12.86,22.5
0.07151,0.0,4.49,0,0.449,6.121,56.8,3.7476,3,247.0,18.5,395.15,8.44,22.2
0.0566,0.0,3.41,0,0.489,7.007,86.3,3.4217,2,270.0,17.8,396.9,5.5,23.6
0.05302,0.0,3.41,0,0.489,7.079,63.1,3.4145,2,270.0,17.8,396.06,5.7,28.7
0.04684,0.0,3.41,0,0.489,6.417,66.1,3.0923,2,270.0,17.8,392.18,8.81,22.6
0.03932,0.0,3.41,0,0.489,6.405,73.9,3.0921,2,270.0,17.8,393.55,8.2,22.0
0.04203,28.0,15.04,0,0.464,6.442,53.6,3.6659,4,270.0,18.2,395.01,8.16,22.9
0.02875,28.0,15.04,0,0.464,6.211,28.9,3.6659,4,270.0,18.2,396.33,6.21,25.0
0.04294,28.0,15.04,0,0.464,6.249,77.3,3.615,4,270.0,18.2,396.9,10.59,20.6
0.12204,0.0,2.89,0,0.445,6.625,57.8,3.4952,2,276.0,18.0,357.98,6.65,28.4
0.11504,0.0,2.89,0,0.445,6.163,69.6,3.4952,2,276.0,18.0,391.83,11.34,21.4
0.12083,0.0,2.89,0,0.445,8.069,76.0,3.4952,2,276.0,18.0,396.9,4.21,38.7
0.08187,0.0,2.89,0,0.445,7.82,36.9,3.4952,2,276.0,18.0,393.53,3.57,43.8
0.0686,0.0,2.89,0,0.445,7.416,62.5,3.4952,2,276.0,18.0,396.9,6.19,33.2
0.14866,0.0,8.56,0,0.52,6.727,79.9,2.7778,5,384.0,20.9,394.76,9.42,27.5
0.11432,0.0,8.56,0,0.52,6.781,71.3,2.8561,5,384.0,20.9,395.58,7.67,26.5
0.22876,0.0,8.56,0,0.52,6.405,85.4,2.7147,5,384.0,20.9,70.8,10.63,18.6
0.21161,0.0,8.56,0,0.52,6.137,87.4,2.7147,5,384.0,20.9,394.47,13.44,19.3
0.1396,0.0,8.56,0,0.52,6.167,90.0,2.421,5,384.0,20.9,392.69,12.33,20.1
0.13262,0.0,8.56,0,0.52,5.851,96.7,2.1069,5,384.0,20.9,394.05,16.47,19.5
0.1712,0.0,8.56,0,0.52,5.836,91.9,2.211,5,384.0,20.9,395.67,18.66,19.5
0.13117,0.0,8.56,0,0.52,6.127,85.2,2.1224,5,384.0,20.9,387.69,14.09,20.4
0.12802,0.0,8.56,0,0.52,6.474,97.1,2.4329,5,384.0,20.9,395.24,12.27,19.8
0.26363,0.0,8.56,0,0.52,6.229,91.2,2.5451,5,384.0,20.9,391.23,15.55,19.4
0.10793,0.0,8.56,0,0.52,6.195,54.4,2.7778,5,384.0,20.9,393.49,13.0,21.7
0.10084,0.0,10.01,0,0.547,6.715,81.6,2.6775,6,432.0,17.8,395.59,10.16,22.8
0.12329,0.0,10.01,0,0.547,5.913,92.9,2.3534,6,432.0,17.8,394.95,16.21,18.8
0.22212,0.0,10.01,0,0.547,6.092,95.4,2.548,6,432.0,17.8,396.9,17.09,18.7
0.14231,0.0,10.01,0,0.547,6.254,84.2,2.2565,6,432.0,17.8,388.74,10.45,18.5
0.17134,0.0,10.01,0,0.547,5.928,88.2,2.4631,6,432.0,17.8,344.91,15.76,18.3
0.13158,0.0,10.01,0,0.547,6.176,72.5,2.7301,6,432.0,17.8,393.3,12.04,21.2
0.15098,0.0,10.01,0,0.547,6.021,82.6,2.7474,6,432.0,17.8,394.51,10.3,19.2
0.13058,0.0,10.01,0,0.547,5.872,73.1,2.4775,6,432.0,17.8,338.63,15.37,20.4
0.14476,0.0,10.01,0,0.547,5.731,65.2,2.7592,6,432.0,17.8,391.5,13.61,19.3
0.06899,0.0,25.65,0,0.581,5.87,69.7,2.2577,2,188.0,19.1,389.15,14.37,22.0
0.07165,0.0,25.65,0,0.581,6.004,84.1,2.1974,2,188.0,19.1,377.67,14.27,20.3
0.09299,0.0,25.65,0,0.581,5.961,92.9,2.0869,2,188.0,19.1,378.09,17.93,20.5
0.15038,0.0,25.65,0,0.581,5.856,97.0,1.9444,2,188.0,19.1,370.31,25.41,17.3
0.09849,0.0,25.65,0,0.581,5.879,95.8,2.0063,2,188.0,19.1,379.38,17.58,18.8
0.16902,0.0,25.65,0,0.581,5.986,88.4,1.9929,2,188.0,19.1,385.02,14.81,21.4
0.38735,0.0,25.65,0,0.581,5.613,95.6,1.7572,2,188.0,19.1,359.29,27.26,15.7
0.25915,0.0,21.89,0,0.624,5.693,96.0,1.7883,4,437.0,21.2,392.11,17.19,16.2
0.32543,0.0,21.89,0,0.624,6.431,98.8,1.8125,4,437.0,21.2,396.9,15.39,18.0
0.88125,0.0,21.89,0,0.624,5.637,94.7,1.9799,4,437.0,21.2,396.9,18.34,14.3
0.34006,0.0,21.89,0,0.624,6.458,98.9,2.1185,4,437.0,21.2,395.04,12.6,19.2
1.19294,0.0,21.89,0,0.624,6.326,97.7,2.271,4,437.0,21.2,396.9,12.26,19.6
0.59005,0.0,21.89,0,0.624,6.372,97.9,2.3274,4,437.0,21.2,385.76,11.12,23.0
0.32982,0.0,21.89,0,0.624,5.822,95.4,2.4699,4,437.0,21.2,388.69,15.03,18.4
0.97617,0.0,21.89,0,0.624,5.757,98.4,2.346,4,437.0,21.2,262.76,17.31,15.6
0.55778,0.0,21.89,0,0.624,6.335,98.2,2.1107,4,437.0,21.2,394.67,16.96,18.1
0.32264,0.0,21.89,0,0.624,5.942,93.5,1.9669,4,437.0,21.2,378.25,16.9,17.4
0.35233,0.0,21.89,0,0.624,6.454,98.4,1.8498,4,437.0,21.2,394.08,14.59,17.1
0.2498,0.0,21.89,0,0.624,5.857,98.2,1.6686,4,437.0,21.2,392.04,21.32,13.3
0.54452,0.0,21.89,0,0.624,6.151,97.9,1.6687,4,437.0,21.2,396.9,18.46,17.8
0.2909,0.0,21.89,0,0.624,6.174,93.6,1.6119,4,437.0,21.2,388.08,24.16,14.0
1.62864,0.0,21.89,0,0.624,5.019,100.0,1.4394,4,437.0,21.2,396.9,34.41,14.4
3.32105,0.0,19.58,1,0.871,5.403,100.0,1.3216,5,403.0,14.7,396.9,26.82,13.4
4.0974,0.0,19.58,0,0.871,5.468,100.0,1.4118,5,403.0,14.7,396.9,26.42,15.6
2.77974,0.0,19.58,0,0.871,4.903,97.8,1.3459,5,403.0,14.7,396.9,29.29,11.8
2.37934,0.0,19.58,0,0.871,6.13,100.0,1.4191,5,403.0,14.7,172.91,27.8,13.8
2.15505,0.0,19.58,0,0.871,5.628,100.0,1.5166,5,403.0,14.7,169.27,16.65,15.6
2.36862,0.0,19.58,0,0.871,4.926,95.7,1.4608,5,403.0,14.7,391.71,29.53,14.6
2.33099,0.0,19.58,0,0.871,5.186,93.8,1.5296,5,403.0,14.7,356.99,28.32,17.8
2.73397,0.0,19.58,0,0.871,5.597,94.9,1.5257,5,403.0,14.7,351.85,21.45,15.4
1.6566,0.0,19.58,0,0.871,6.122,97.3,1.618,5,403.0,14.7,372.8,14.1,21.5
1.49632,0.0,19.58,0,0.871,5.404,100.0,1.5916,5,403.0,14.7,341.6,13.28,19.6
1.12658,0.0,19.58,1,0.871,5.012,88.0,1.6102,5,403.0,14.7,343.28,12.12,15.3
2.14918,0.0,19.58,0,0.871,5.709,98.5,1.6232,5,403.0,14.7,261.95,15.79,19.4
1.41385,0.0,19.58,1,0.871,6.129,96.0,1.7494,5,403.0,14.7,321.02,15.12,17.0
3.53501,0.0,19.58,1,0.871,6.152,82.6,1.7455,5,403.0,14.7,88.01,15.02,15.6
2.44668,0.0,19.58,0,0.871,5.272,94.0,1.7364,5,403.0,14.7,88.63,16.14,13.1
1.22358,0.0,19.58,0,0.605,6.943,97.4,1.8773,5,403.0,14.7,363.43,4.59,41.3
1.34284,0.0,19.58,0,0.605,6.066,100.0,1.7573,5,403.0,14.7,353.89,6.43,24.3
1.42502,0.0,19.58,0,0.871,6.51,100.0,1.7659,5,403.0,14.7,364.31,7.39,23.3
1.27346,0.0,19.58,1,0.605,6.25,92.6,1.7984,5,403.0,14.7,338.92,5.5,27.0
1.46336,0.0,19.58,0,0.605,7.489,90.8,1.9709,5,403.0,14.7,374.43,1.73,50.0
1.83377,0.0,19.58,1,0.605,7.802,98.2,2.0407,5,403.0,14.7,389.61,1.92,50.0
1.51902,0.0,19.58,1,0.605,8.375,93.9,2.162,5,403.0,14.7,388.45,3.32,50.0
2.24236,0.0,19.58,0,0.605,5.854,91.8,2.422,5,403.0,14.7,395.11,11.64,22.7
2.924,0.0,19.58,0,0.605,6.101,93.0,2.2834,5,403.0,14.7,240.16,9.81,25.0
2.01019,0.0,19.58,0,0.605,7.929,96.2,2.0459,5,403.0,14.7,369.3,3.7,50.0
1.80028,0.0,19.58,0,0.605,5.877,79.2,2.4259,5,403.0,14.7,227.61,12.14,23.8
2.3004,0.0,19.58,0,0.605,6.319,96.1,2.1,5,403.0,14.7,297.09,11.1,23.8
2.44953,0.0,19.58,0,0.605,6.402,95.2,2.2625,5,403.0,14.7,330.04,11.32,22.3
1.20742,0.0,19.58,0,0.605,5.875,94.6,2.4259,5,403.0,14.7,292.29,14.43,17.4
2.3139,0.0,19.58,0,0.605,5.88,97.3,2.3887,5,403.0,14.7,348.13,12.03,19.1
0.13914,0.0,4.05,0,0.51,5.572,88.5,2.5961,5,296.0,16.6,396.9,14.69,23.1
0.09178,0.0,4.05,0,0.51,6.416,84.1,2.6463,5,296.0,16.6,395.5,9.04,23.6
0.08447,0.0,4.05,0,0.51,5.859,68.7,2.7019,5,296.0,16.6,393.23,9.64,22.6
0.06664,0.0,4.05,0,0.51,6.546,33.1,3.1323,5,296.0,16.6,390.96,5.33,29.4
0.07022,0.0,4.05,0,0.51,6.02,47.2,3.5549,5,296.0,16.6,393.23,10.11,23.2
0.05425,0.0,4.05,0,0.51,6.315,73.4,3.3175,5,296.0,16.6,395.6,6.29,24.6
0.06642,0.0,4.05,0,0.51,6.86,74.4,2.9153,5,296.0,16.6,391.27,6.92,29.9
0.0578,0.0,2.46,0,0.488,6.98,58.4,2.829,3,193.0,17.8,396.9,5.04,37.2
0.06588,0.0,2.46,0,0.488,7.765,83.3,2.741,3,193.0,17.8,395.56,7.56,39.8
0.06888,0.0,2.46,0,0.488,6.144,62.2,2.5979,3,193.0,17.8,396.9,9.45,36.2
0.09103,0.0,2.46,0,0.488,7.155,92.2,2.7006,3,193.0,17.8,394.12,4.82,37.9
0.10008,0.0,2.46,0,0.488,6.563,95.6,2.847,3,193.0,17.8,396.9,5.68,32.5
0.08308,0.0,2.46,0,0.488,5.604,89.8,2.9879,3,193.0,17.8,391.0,13.98,26.4
0.06047,0.0,2.46,0,0.488,6.153,68.8,3.2797,3,193.0,17.8,387.11,13.15,29.6
0.05602,0.0,2.46,0,0.488,7.831,53.6,3.1992,3,193.0,17.8,392.63,4.45,50.0
0.07875,45.0,3.44,0,0.437,6.782,41.1,3.7886,5,398.0,15.2,393.87,6.68,32.0
0.12579,45.0,3.44,0,0.437,6.556,29.1,4.5667,5,398.0,15.2,382.84,4.56,29.8
0.0837,45.0,3.44,0,0.437,7.185,38.9,4.5667,5,398.0,15.2,396.9,5.39,34.9
0.09068,45.0,3.44,0,0.437,6.951,21.5,6.4798,5,398.0,15.2,377.68,5.1,37.0
0.06911,45.0,3.44,0,0.437,6.739,30.8,6.4798,5,398.0,15.2,389.71,4.69,30.5
0.08664,45.0,3.44,0,0.437,7.178,26.3,6.4798,5,398.0,15.2,390.49,2.87,36.4
0.02187,60.0,2.93,0,0.401,6.8,9.9,6.2196,1,265.0,15.6,393.37,5.03,31.1
0.01439,60.0,2.93,0,0.401,6.604,18.8,6.2196,1,265.0,15.6,376.7,4.38,29.1
0.01381,80.0,0.46,0,0.422,7.875,32.0,5.6484,4,255.0,14.4,394.23,2.97,50.0
0.04011,80.0,1.52,0,0.404,7.287,34.1,7.309,2,329.0,12.6,396.9,4.08,33.3
0.04666,80.0,1.52,0,0.404,7.107,36.6,7.309,2,329.0,12.6,354.31,8.61,30.3
0.03768,80.0,1.52,0,0.404,7.274,38.3,7.309,2,329.0,12.6,392.2,6.62,34.6
0.0315,95.0,1.47,0,0.403,6.975,15.3,7.6534,3,402.0,17.0,396.9,4.56,34.9
0.01778,95.0,1.47,0,0.403,7.135,13.9,7.6534,3,402.0,17.0,384.3,4.45,32.9
0.03445,82.5,2.03,0,0.415,6.162,38.4,6.27,2,348.0,14.7,393.77,7.43,24.1
0.02177,82.5,2.03,0,0.415,7.61,15.7,6.27,2,348.0,14.7,395.38,3.11,42.3
0.0351,95.0,2.68,0,0.4161,7.853,33.2,5.118,4,224.0,14.7,392.78,3.81,48.5
0.02009,95.0,2.68,0,0.4161,8.034,31.9,5.118,4,224.0,14.7,390.55,2.88,50.0
0.13642,0.0,10.59,0,0.489,5.891,22.3,3.9454,4,277.0,18.6,396.9,10.87,22.6
0.22969,0.0,10.59,0,0.489,6.326,52.5,4.3549,4,277.0,18.6,394.87,10.97,24.4
0.25199,0.0,10.59,0,0.489,5.783,72.7,4.3549,4,277.0,18.6,389.43,18.06,22.5
0.13587,0.0,10.59,1,0.489,6.064,59.1,4.2392,4,277.0,18.6,381.32,14.66,24.4
0.43571,0.0,10.59,1,0.489,5.344,100.0,3.875,4,277.0,18.6,396.9,23.09,20.0
0.17446,0.0,10.59,1,0.489,5.96,92.1,3.8771,4,277.0,18.6,393.25,17.27,21.7
0.37578,0.0,10.59,1,0.489,5.404,88.6,3.665,4,277.0,18.6,395.24,23.98,19.3
0.21719,0.0,10.59,1,0.489,5.807,53.8,3.6526,4,277.0,18.6,390.94,16.03,22.4
0.14052,0.0,10.59,0,0.489,6.375,32.3,3.9454,4,277.0,18.6,385.81,9.38,28.1
0.28955,0.0,10.59,0,0.489,5.412,9.8,3.5875,4,277.0,18.6,348.93,29.55,23.7
0.19802,0.0,10.59,0,0.489,6.182,42.4,3.9454,4,277.0,18.6,393.63,9.47,25.0
0.0456,0.0,13.89,1,0.55,5.888,56.0,3.1121,5,276.0,16.4,392.8,13.51,23.3
0.07013,0.0,13.89,0,0.55,6.642,85.1,3.4211,5,276.0,16.4,392.78,9.69,28.7
0.11069,0.0,13.89,1,0.55,5.951,93.8,2.8893,5,276.0,16.4,396.9,17.92,21.5
0.11425,0.0,13.89,1,0.55,6.373,92.4,3.3633,5,276.0,16.4,393.74,10.5,23.0
0.35809,0.0,6.2,1,0.507,6.951,88.5,2.8617,8,307.0,17.4,391.7,9.71,26.7
0.40771,0.0,6.2,1,0.507,6.164,91.3,3.048,8,307.0,17.4,395.24,21.46,21.7
0.62356,0.0,6.2,1,0.507,6.879,77.7,3.2721,8,307.0,17.4,390.39,9.93,27.5
0.6147,0.0,6.2,0,0.507,6.618,80.8,3.2721,8,307.0,17.4,396.9,7.6,30.1
0.31533,0.0,6.2,0,0.504,8.266,78.3,2.8944,8,307.0,17.4,385.05,4.14,44.8
0.52693,0.0,6.2,0,0.504,8.725,83.0,2.8944,8,307.0,17.4,382.0,4.63,50.0
0.38214,0.0,6.2,0,0.504,8.04,86.5,3.2157,8,307.0,17.4,387.38,3.13,37.6
0.41238,0.0,6.2,0,0.504,7.163,79.9,3.2157,8,307.0,17.4,372.08,6.36,31.6
0.29819,0.0,6.2,0,0.504,7.686,17.0,3.3751,8,307.0,17.4,377.51,3.92,46.7
0.44178,0.0,6.2,0,0.504,6.552,21.4,3.3751,8,307.0,17.4,380.34,3.76,31.5
0.537,0.0,6.2,0,0.504,5.981,68.1,3.6715,8,307.0,17.4,378.35,11.65,24.3
0.46296,0.0,6.2,0,0.504,7.412,76.9,3.6715,8,307.0,17.4,376.14,5.25,31.7
0.57529,0.0,6.2,0,0.507,8.337,73.3,3.8384,8,307.0,17.4,385.91,2.47,41.7
0.33147,0.0,6.2,0,0.507,8.247,70.4,3.6519,8,307.0,17.4,378.95,3.95,48.3
0.44791,0.0,6.2,1,0.507,6.726,66.5,3.6519,8,307.0,17.4,360.2,8.05,29.0
0.33045,0.0,6.2,0,0.507,6.086,61.5,3.6519,8,307.0,17.4,376.75,10.88,24.0
0.52058,0.0,6.2,1,0.507,6.631,76.5,4.148,8,307.0,17.4,388.45,9.54,25.1
0.51183,0.0,6.2,0,0.507,7.358,71.6,4.148,8,307.0,17.4,390.07,4.73,31.5
0.08244,30.0,4.93,0,0.428,6.481,18.5,6.1899,6,300.0,16.6,379.41,6.36,23.7
0.09252,30.0,4.93,0,0.428,6.606,42.2,6.1899,6,300.0,16.6,383.78,7.37,23.3
0.11329,30.0,4.93,0,0.428,6.897,54.3,6.3361,6,300.0,16.6,391.25,11.38,22.0
0.10612,30.0,4.93,0,0.428,6.095,65.1,6.3361,6,300.0,16.6,394.62,12.4,20.1
0.1029,30.0,4.93,0,0.428,6.358,52.9,7.0355,6,300.0,16.6,372.75,11.22,22.2
0.12757,30.0,4.93,0,0.428,6.393,7.8,7.0355,6,300.0,16.6,374.71,5.19,23.7
0.20608,22.0,5.86,0,0.431,5.593,76.5,7.9549,7,330.0,19.1,372.49,12.5,17.6
0.19133,22.0,5.86,0,0.431,5.605,70.2,7.9549,7,330.0,19.1,389.13,18.46,18.5
0.33983,22.0,5.86,0,0.431,6.108,34.9,8.0555,7,330.0,19.1,390.18,9.16,24.3
0.19657,22.0,5.86,0,0.431,6.226,79.2,8.0555,7,330.0,19.1,376.14,10.15,20.5
0.16439,22.0,5.86,0,0.431,6.433,49.1,7.8265,7,330.0,19.1,374.71,9.52,24.5
0.19073,22.0,5.86,0,0.431,6.718,17.5,7.8265,7,330.0,19.1,393.74,6.56,26.2
0.1403,22.0,5.86,0,0.431,6.487,13.0,7.3967,7,330.0,19.1,396.28,5.9,24.4
0.21409,22.0,5.86,0,0.431,6.438,8.9,7.3967,7,330.0,19.1,377.07,3.59,24.8
0.08221,22.0,5.86,0,0.431,6.957,6.8,8.9067,7,330.0,19.1,386.09,3.53,29.6
0.36894,22.0,5.86,0,0.431,8.259,8.4,8.9067,7,330.0,19.1,396.9,3.54,42.8
0.04819,80.0,3.64,0,0.392,6.108,32.0,9.2203,1,315.0,16.4,392.89,6.57,21.9
0.03548,80.0,3.64,0,0.392,5.876,19.1,9.2203,1,315.0,16.4,395.18,9.25,20.9
0.01538,90.0,3.75,0,0.394,7.454,34.2,6.3361,3,244.0,15.9,386.34,3.11,44.0
0.61154,20.0,3.97,0,0.647,8.704,86.9,1.801,5,264.0,13.0,389.7,5.12,50.0
0.66351,20.0,3.97,0,0.647,7.333,100.0,1.8946,5,264.0,13.0,383.29,7.79,36.0
0.65665,20.0,3.97,0,0.647,6.842,100.0,2.0107,5,264.0,13.0,391.93,6.9,30.1
0.54011,20.0,3.97,0,0.647,7.203,81.8,2.1121,5,264.0,13.0,392.8,9.59,33.8
0.53412,20.0,3.97,0,0.647,7.52,89.4,2.1398,5,264.0,13.0,388.37,7.26,43.1
0.52014,20.0,3.97,0,0.647,8.398,91.5,2.2885,5,264.0,13.0,386.86,5.91,48.8
0.82526,20.0,3.97,0,0.647,7.327,94.5,2.0788,5,264.0,13.0,393.42,11.25,31.0
0.55007,20.0,3.97,0,0.647,7.206,91.6,1.9301,5,264.0,13.0,387.89,8.1,36.5
0.76162,20.0,3.97,0,0.647,5.56,62.8,1.9865,5,264.0,13.0,392.4,10.45,22.8
0.7857,20.0,3.97,0,0.647,7.014,84.6,2.1329,5,264.0,13.0,384.07,14.79,30.7
0.57834,20.0,3.97,0,0.575,8.297,67.0,2.4216,5,264.0,13.0,384.54,7.44,50.0
0.5405,20.0,3.97,0,0.575,7.47,52.6,2.872,5,264.0,13.0,390.3,3.16,43.5
0.09065,20.0,6.96,1,0.464,5.92,61.5,3.9175,3,223.0,18.6,391.34,13.65,20.7
0.29916,20.0,6.96,0,0.464,5.856,42.1,4.429,3,223.0,18.6,388.65,13.0,21.1
0.16211,20.0,6.96,0,0.464,6.24,16.3,4.429,3,223.0,18.6,396.9,6.59,25.2
0.1146,20.0,6.96,0,0.464,6.538,58.7,3.9175,3,223.0,18.6,394.96,7.73,24.4
0.22188,20.0,6.96,1,0.464,7.691,51.8,4.3665,3,223.0,18.6,390.77,6.58,35.2
0.05644,40.0,6.41,1,0.447,6.758,32.9,4.0776,4,254.0,17.6,396.9,3.53,32.4
0.09604,40.0,6.41,0,0.447,6.854,42.8,4.2673,4,254.0,17.6,396.9,2.98,32.0
0.10469,40.0,6.41,1,0.447,7.267,49.0,4.7872,4,254.0,17.6,389.25,6.05,33.2
0.06127,40.0,6.41,1,0.447,6.826,27.6,4.8628,4,254.0,17.6,393.45,4.16,33.1
0.07978,40.0,6.41,0,0.447,6.482,32.1,4.1403,4,254.0,17.6,396.9,7.19,29.1
0.21038,20.0,3.33,0,0.4429,6.812,32.2,4.1007,5,216.0,14.9,396.9,4.85,35.1
0.03578,20.0,3.33,0,0.4429,7.82,64.5,4.6947,5,216.0,14.9,387.31,3.76,45.4
0.03705,20.0,3.33,0,0.4429,6.968,37.2,5.2447,5,216.0,14.9,392.23,4.59,35.4
0.06129,20.0,3.33,1,0.4429,7.645,49.7,5.2119,5,216.0,14.9,377.07,3.01,46.0
0.01501,90.0,1.21,1,0.401,7.923,24.8,5.885,1,198.0,13.6,395.52,3.16,50.0
0.00906,90.0,2.97,0,0.4,7.088,20.8,7.3073,1,285.0,15.3,394.72,7.85,32.2
0.01096,55.0,2.25,0,0.389,6.453,31.9,7.3073,1,300.0,15.3,394.72,8.23,22.0
0.01965,80.0,1.76,0,0.385,6.23,31.5,9.0892,1,241.0,18.2,341.6,12.93,20.1
0.03871,52.5,5.32,0,0.405,6.209,31.3,7.3172,6,293.0,16.6,396.9,7.14,23.2
0.0459,52.5,5.32,0,0.405,6.315,45.6,7.3172,6,293.0,16.6,396.9,7.6,22.3
0.04297,52.5,5.32,0,0.405,6.565,22.9,7.3172,6,293.0,16.6,371.72,9.51,24.8
0.03502,80.0,4.95,0,0.411,6.861,27.9,5.1167,4,245.0,19.2,396.9,3.33,28.5
0.07886,80.0,4.95,0,0.411,7.148,27.7,5.1167,4,245.0,19.2,396.9,3.56,37.3
0.03615,80.0,4.95,0,0.411,6.63,23.4,5.1167,4,245.0,19.2,396.9,4.7,27.9
0.08265,0.0,13.92,0,0.437,6.127,18.4,5.5027,4,289.0,16.0,396.9,8.58,23.9
0.08199,0.0,13.92,0,0.437,6.009,42.3,5.5027,4,289.0,16.0,396.9,10.4,21.7
0.12932,0.0,13.92,0,0.437,6.678,31.1,5.9604,4,289.0,16.0,396.9,6.27,28.6
0.05372,0.0,13.92,0,0.437,6.549,51.0,5.9604,4,289.0,16.0,392.85,7.39,27.1
0.14103,0.0,13.92,0,0.437,5.79,58.0,6.32,4,289.0,16.0,396.9,15.84,20.3
0.06466,70.0,2.24,0,0.4,6.345,20.1,7.8278,5,358.0,14.8,368.24,4.97,22.5
0.05561,70.0,2.24,0,0.4,7.041,10.0,7.8278,5,358.0,14.8,371.58,4.74,29.0
0.04417,70.0,2.24,0,0.4,6.871,47.4,7.8278,5,358.0,14.8,390.86,6.07,24.8
0.03537,34.0,6.09,0,0.433,6.59,40.4,5.4917,7,329.0,16.1,395.75,9.5,22.0
0.09266,34.0,6.09,0,0.433,6.495,18.4,5.4917,7,329.0,16.1,383.61,8.67,26.4
0.1,34.0,6.09,0,0.433,6.982,17.7,5.4917,7,329.0,16.1,390.43,4.86,33.1
0.05515,33.0,2.18,0,0.472,7.236,41.1,4.022,7,222.0,18.4,393.68,6.93,36.1
0.05479,33.0,2.18,0,0.472,6.616,58.1,3.37,7,222.0,18.4,393.36,8.93,28.4
0.07503,33.0,2.18,0,0.472,7.42,71.9,3.0992,7,222.0,18.4,396.9,6.47,33.4
0.04932,33.0,2.18,0,0.472,6.849,70.3,3.1827,7,222.0,18.4,396.9,7.53,28.2
0.49298,0.0,9.9,0,0.544,6.635,82.5,3.3175,4,304.0,18.4,396.9,4.54,22.8
0.3494,0.0,9.9,0,0.544,5.972,76.7,3.1025,4,304.0,18.4,396.24,9.97,20.3
2.63548,0.0,9.9,0,0.544,4.973,37.8,2.5194,4,304.0,18.4,350.45,12.64,16.1
0.79041,0.0,9.9,0,0.544,6.122,52.8,2.6403,4,304.0,18.4,396.9,5.98,22.1
0.26169,0.0,9.9,0,0.544,6.023,90.4,2.834,4,304.0,18.4,396.3,11.72,19.4
0.26938,0.0,9.9,0,0.544,6.266,82.8,3.2628,4,304.0,18.4,393.39,7.9,21.6
0.3692,0.0,9.9,0,0.544,6.567,87.3,3.6023,4,304.0,18.4,395.69,9.28,23.8
0.25356,0.0,9.9,0,0.544,5.705,77.7,3.945,4,304.0,18.4,396.42,11.5,16.2
0.31827,0.0,9.9,0,0.544,5.914,83.2,3.9986,4,304.0,18.4,390.7,18.33,17.8
0.24522,0.0,9.9,0,0.544,5.782,71.7,4.0317,4,304.0,18.4,396.9,15.94,19.8
0.40202,0.0,9.9,0,0.544,6.382,67.2,3.5325,4,304.0,18.4,395.21,10.36,23.1
0.47547,0.0,9.9,0,0.544,6.113,58.8,4.0019,4,304.0,18.4,396.23,12.73,21.0
0.1676,0.0,7.38,0,0.493,6.426,52.3,4.5404,5,287.0,19.6,396.9,7.2,23.8
0.18159,0.0,7.38,0,0.493,6.376,54.3,4.5404,5,287.0,19.6,396.9,6.87,23.1
0.35114,0.0,7.38,0,0.493,6.041,49.9,4.7211,5,287.0,19.6,396.9,7.7,20.4
0.28392,0.0,7.38,0,0.493,5.708,74.3,4.7211,5,287.0,19.6,391.13,11.74,18.5
0.34109,0.0,7.38,0,0.493,6.415,40.1,4.7211,5,287.0,19.6,396.9,6.12,25.0
0.19186,0.0,7.38,0,0.493,6.431,14.7,5.4159,5,287.0,19.6,393.68,5.08,24.6
0.30347,0.0,7.38,0,0.493,6.312,28.9,5.4159,5,287.0,19.6,396.9,6.15,23.0
0.24103,0.0,7.38,0,0.493,6.083,43.7,5.4159,5,287.0,19.6,396.9,12.79,22.2
0.06617,0.0,3.24,0,0.46,5.868,25.8,5.2146,4,430.0,16.9,382.44,9.97,19.3
0.06724,0.0,3.24,0,0.46,6.333,17.2,5.2146,4,430.0,16.9,375.21,7.34,22.6
0.04544,0.0,3.24,0,0.46,6.144,32.2,5.8736,4,430.0,16.9,368.57,9.09,19.8
0.05023,35.0,6.06,0,0.4379,5.706,28.4,6.6407,1,304.0,16.9,394.02,12.43,17.1
0.03466,35.0,6.06,0,0.4379,6.031,23.3,6.6407,1,304.0,16.9,362.25,7.83,19.4
0.05083,0.0,5.19,0,0.515,6.316,38.1,6.4584,5,224.0,20.2,389.71,5.68,22.2
0.03738,0.0,5.19,0,0.515,6.31,38.5,6.4584,5,224.0,20.2,389.4,6.75,20.7
0.03961,0.0,5.19,0,0.515,6.037,34.5,5.9853,5,224.0,20.2,396.9,8.01,21.1
0.03427,0.0,5.19,0,0.515,5.869,46.3,5.2311,5,224.0,20.2,396.9,9.8,19.5
0.03041,0.0,5.19,0,0.515,5.895,59.6,5.615,5,224.0,20.2,394.81,10.56,18.5
0.03306,0.0,5.19,0,0.515,6.059,37.3,4.8122,5,224.0,20.2,396.14,8.51,20.6
0.05497,0.0,5.19,0,0.515,5.985,45.4,4.8122,5,224.0,20.2,396.9,9.74,19.0
0.06151,0.0,5.19,0,0.515,5.968,58.5,4.8122,5,224.0,20.2,396.9,9.29,18.7
0.01301,35.0,1.52,0,0.442,7.241,49.3,7.0379,1,284.0,15.5,394.74,5.49,32.7
0.02498,0.0,1.89,0,0.518,6.54,59.7,6.2669,1,422.0,15.9,389.96,8.65,16.5
0.02543,55.0,3.78,0,0.484,6.696,56.4,5.7321,5,370.0,17.6,396.9,7.18,23.9
0.03049,55.0,3.78,0,0.484,6.874,28.1,6.4654,5,370.0,17.6,387.97,4.61,31.2
0.03113,0.0,4.39,0,0.442,6.014,48.5,8.0136,3,352.0,18.8,385.64,10.53,17.5
0.06162,0.0,4.39,0,0.442,5.898,52.3,8.0136,3,352.0,18.8,364.61,12.67,17.2
0.0187,85.0,4.15,0,0.429,6.516,27.7,8.5353,4,351.0,17.9,392.43,6.36,23.1
0.01501,80.0,2.01,0,0.435,6.635,29.7,8.344,4,280.0,17.0,390.94,5.99,24.5
0.02899,40.0,1.25,0,0.429,6.939,34.5,8.7921,1,335.0,19.7,389.85,5.89,26.6
0.06211,40.0,1.25,0,0.429,6.49,44.4,8.7921,1,335.0,19.7,396.9,5.98,22.9
0.0795,60.0,1.69,0,0.411,6.579,35.9,10.7103,4,411.0,18.3,370.78,5.49,24.1
0.07244,60.0,1.69,0,0.411,5.884,18.5,10.7103,4,411.0,18.3,392.33,7.79,18.6
0.01709,90.0,2.02,0,0.41,6.728,36.1,12.1265,5,187.0,17.0,384.46,4.5,30.1
0.04301,80.0,1.91,0,0.413,5.663,21.9,10.5857,4,334.0,22.0,382.8,8.05,18.2
0.10659,80.0,1.91,0,0.413,5.936,19.5,10.5857,4,334.0,22.0,376.04,5.57,20.6
8.98296,0.0,18.1,1,0.77,6.212,97.4,2.1222,24,666.0,20.2,377.73,17.6,17.8
3.8497,0.0,18.1,1,0.77,6.395,91.0,2.5052,24,666.0,20.2,391.34,13.27,21.7
5.20177,0.0,18.1,1,0.77,6.127,83.4,2.7227,24,666.0,20.2,395.43,11.48,22.7
4.26131,0.0,18.1,0,0.77,6.112,81.3,2.5091,24,666.0,20.2,390.74,12.67,22.6
4.54192,0.0,18.1,0,0.77,6.398,88.0,2.5182,24,666.0,20.2,374.56,7.79,25.0
3.83684,0.0,18.1,0,0.77,6.251,91.1,2.2955,24,666.0,20.2,350.65,14.19,19.9
3.67822,0.0,18.1,0,0.77,5.362,96.2,2.1036,24,666.0,20.2,380.79,10.19,20.8
4.22239,0.0,18.1,1,0.77,5.803,89.0,1.9047,24,666.0,20.2,353.04,14.64,16.8
3.47428,0.0,18.1,1,0.718,8.78,82.9,1.9047,24,666.0,20.2,354.55,5.29,21.9
4.55587,0.0,18.1,0,0.718,3.561,87.9,1.6132,24,666.0,20.2,354.7,7.12,27.5
3.69695,0.0,18.1,0,0.718,4.963,91.4,1.7523,24,666.0,20.2,316.03,14.0,21.9
13.5222,0.0,18.1,0,0.631,3.863,100.0,1.5106,24,666.0,20.2,131.42,13.33,23.1
4.89822,0.0,18.1,0,0.631,4.97,100.0,1.3325,24,666.0,20.2,375.52,3.26,50.0
5.66998,0.0,18.1,1,0.631,6.683,96.8,1.3567,24,666.0,20.2,375.33,3.73,50.0
6.53876,0.0,18.1,1,0.631,7.016,97.5,1.2024,24,666.0,20.2,392.05,2.96,50.0
9.2323,0.0,18.1,0,0.631,6.216,100.0,1.1691,24,666.0,20.2,366.15,9.53,50.0
8.26725,0.0,18.1,1,0.668,5.875,89.6,1.1296,24,666.0,20.2,347.88,8.88,50.0
11.1081,0.0,18.1,0,0.668,4.906,100.0,1.1742,24,666.0,20.2,396.9,34.77,13.8
18.4982,0.0,18.1,0,0.668,4.138,100.0,1.137,24,666.0,20.2,396.9,37.97,13.8
19.6091,0.0,18.1,0,0.671,7.313,97.9,1.3163,24,666.0,20.2,396.9,13.44,15.0
15.288,0.0,18.1,0,0.671,6.649,93.3,1.3449,24,666.0,20.2,363.02,23.24,13.9
9.82349,0.0,18.1,0,0.671,6.794,98.8,1.358,24,666.0,20.2,396.9,21.24,13.3
23.6482,0.0,18.1,0,0.671,6.38,96.2,1.3861,24,666.0,20.2,396.9,23.69,13.1
17.8667,0.0,18.1,0,0.671,6.223,100.0,1.3861,24,666.0,20.2,393.74,21.78,10.2
88.9762,0.0,18.1,0,0.671,6.968,91.9,1.4165,24,666.0,20.2,396.9,17.21,10.4
15.8744,0.0,18.1,0,0.671,6.545,99.1,1.5192,24,666.0,20.2,396.9,21.08,10.9
9.18702,0.0,18.1,0,0.7,5.536,100.0,1.5804,24,666.0,20.2,396.9,23.6,11.3
7.99248,0.0,18.1,0,0.7,5.52,100.0,1.5331,24,666.0,20.2,396.9,24.56,12.3
20.0849,0.0,18.1,0,0.7,4.368,91.2,1.4395,24,666.0,20.2,285.83,30.63,8.8
16.8118,0.0,18.1,0,0.7,5.277,98.1,1.4261,24,666.0,20.2,396.9,30.81,7.2
24.3938,0.0,18.1,0,0.7,4.652,100.0,1.4672,24,666.0,20.2,396.9,28.28,10.5
22.5971,0.0,18.1,0,0.7,5.0,89.5,1.5184,24,666.0,20.2,396.9,31.99,7.4
14.3337,0.0,18.1,0,0.7,4.88,100.0,1.5895,24,666.0,20.2,372.92,30.62,10.2
8.15174,0.0,18.1,0,0.7,5.39,98.9,1.7281,24,666.0,20.2,396.9,20.85,11.5
6.96215,0.0,18.1,0,0.7,5.713,97.0,1.9265,24,666.0,20.2,394.43,17.11,15.1
5.29305,0.0,18.1,0,0.7,6.051,82.5,2.1678,24,666.0,20.2,378.38,18.76,23.2
11.5779,0.0,18.1,0,0.7,5.036,97.0,1.77,24,666.0,20.2,396.9,25.68,9.7
8.64476,0.0,18.1,0,0.693,6.193,92.6,1.7912,24,666.0,20.2,396.9,15.17,13.8
13.3598,0.0,18.1,0,0.693,5.887,94.7,1.7821,24,666.0,20.2,396.9,16.35,12.7
8.71675,0.0,18.1,0,0.693,6.471,98.8,1.7257,24,666.0,20.2,391.98,17.12,13.1
5.87205,0.0,18.1,0,0.693,6.405,96.0,1.6768,24,666.0,20.2,396.9,19.37,12.5
7.67202,0.0,18.1,0,0.693,5.747,98.9,1.6334,24,666.0,20.2,393.1,19.92,8.5
38.3518,0.0,18.1,0,0.693,5.453,100.0,1.4896,24,666.0,20.2,396.9,30.59,5.0
9.91655,0.0,18.1,0,0.693,5.852,77.8,1.5004,24,666.0,20.2,338.16,29.97,6.3
25.0461,0.0,18.1,0,0.693,5.987,100.0,1.5888,24,666.0,20.2,396.9,26.77,5.6
14.2362,0.0,18.1,0,0.693,6.343,100.0,1.5741,24,666.0,20.2,396.9,20.32,7.2
9.59571,0.0,18.1,0,0.693,6.404,100.0,1.639,24,666.0,20.2,376.11,20.31,12.1
24.8017,0.0,18.1,0,0.693,5.349,96.0,1.7028,24,666.0,20.2,396.9,19.77,8.3
41.5292,0.0,18.1,0,0.693,5.531,85.4,1.6074,24,666.0,20.2,329.46,27.38,8.5
67.9208,0.0,18.1,0,0.693,5.683,100.0,1.4254,24,666.0,20.2,384.97,22.98,5.0
20.7162,0.0,18.1,0,0.659,4.138,100.0,1.1781,24,666.0,20.2,370.22,23.34,11.9
11.9511,0.0,18.1,0,0.659,5.608,100.0,1.2852,24,666.0,20.2,332.09,12.13,27.9
7.40389,0.0,18.1,0,0.597,5.617,97.9,1.4547,24,666.0,20.2,314.64,26.4,17.2
14.4383,0.0,18.1,0,0.597,6.852,100.0,1.4655,24,666.0,20.2,179.36,19.78,27.5
51.1358,0.0,18.1,0,0.597,5.757,100.0,1.413,24,666.0,20.2,2.6,10.11,15.0
14.0507,0.0,18.1,0,0.597,6.657,100.0,1.5275,24,666.0,20.2,35.05,21.22,17.2
18.811,0.0,18.1,0,0.597,4.628,100.0,1.5539,24,666.0,20.2,28.79,34.37,17.9
28.6558,0.0,18.1,0,0.597,5.155,100.0,1.5894,24,666.0,20.2,210.97,20.08,16.3
45.7461,0.0,18.1,0,0.693,4.519,100.0,1.6582,24,666.0,20.2,88.27,36.98,7.0
18.0846,0.0,18.1,0,0.679,6.434,100.0,1.8347,24,666.0,20.2,27.25,29.05,7.2
10.8342,0.0,18.1,0,0.679,6.782,90.8,1.8195,24,666.0,20.2,21.57,25.79,7.5
25.9406,0.0,18.1,0,0.679,5.304,89.1,1.6475,24,666.0,20.2,127.36,26.64,10.4
73.5341,0.0,18.1,0,0.679,5.957,100.0,1.8026,24,666.0,20.2,16.45,20.62,8.8
11.8123,0.0,18.1,0,0.718,6.824,76.5,1.794,24,666.0,20.2,48.45,22.74,8.4
11.0874,0.0,18.1,0,0.718,6.411,100.0,1.8589,24,666.0,20.2,318.75,15.02,16.7
7.02259,0.0,18.1,0,0.718,6.006,95.3,1.8746,24,666.0,20.2,319.98,15.7,14.2
12.0482,0.0,18.1,0,0.614,5.648,87.6,1.9512,24,666.0,20.2,291.55,14.1,20.8
7.05042,0.0,18.1,0,0.614,6.103,85.1,2.0218,24,666.0,20.2,2.52,23.29,13.4
8.79212,0.0,18.1,0,0.584,5.565,70.6,2.0635,24,666.0,20.2,3.65,17.16,11.7
15.8603,0.0,18.1,0,0.679,5.896,95.4,1.9096,24,666.0,20.2,7.68,24.39,8.3
12.2472,0.0,18.1,0,0.584,5.837,59.7,1.9976,24,666.0,20.2,24.65,15.69,10.2
37.6619,0.0,18.1,0,0.679,6.202,78.7,1.8629,24,666.0,20.2,18.82,14.52,10.9
7.36711,0.0,18.1,0,0.679,6.193,78.1,1.9356,24,666.0,20.2,96.73,21.52,11.0
9.33889,0.0,18.1,0,0.679,6.38,95.6,1.9682,24,666.0,20.2,60.72,24.08,9.5
8.49213,0.0,18.1,0,0.584,6.348,86.1,2.0527,24,666.0,20.2,83.45,17.64,14.5
10.0623,0.0,18.1,0,0.584,6.833,94.3,2.0882,24,666.0,20.2,81.33,19.69,14.1
6.44405,0.0,18.1,0,0.584,6.425,74.8,2.2004,24,666.0,20.2,97.95,12.03,16.1
5.58107,0.0,18.1,0,0.713,6.436,87.9,2.3158,24,666.0,20.2,100.19,16.22,14.3
13.9134,0.0,18.1,0,0.713,6.208,95.0,2.2222,24,666.0,20.2,100.63,15.17,11.7
11.1604,0.0,18.1,0,0.74,6.629,94.6,2.1247,24,666.0,20.2,109.85,23.27,13.4
14.4208,0.0,18.1,0,0.74,6.461,93.3,2.0026,24,666.0,20.2,27.49,18.05,9.6
15.1772,0.0,18.1,0,0.74,6.152,100.0,1.9142,24,666.0,20.2,9.32,26.45,8.7
13.6781,0.0,18.1,0,0.74,5.935,87.9,1.8206,24,666.0,20.2,68.95,34.02,8.4
9.39063,0.0,18.1,0,0.74,5.627,93.9,1.8172,24,666.0,20.2,396.9,22.88,12.8
22.0511,0.0,18.1,0,0.74,5.818,92.4,1.8662,24,666.0,20.2,391.45,22.11,10.5
9.72418,0.0,18.1,0,0.74,6.406,97.2,2.0651,24,666.0,20.2,385.96,19.52,17.1
5.66637,0.0,18.1,0,0.74,6.219,100.0,2.0048,24,666.0,20.2,395.69,16.59,18.4
9.96654,0.0,18.1,0,0.74,6.485,100.0,1.9784,24,666.0,20.2,386.73,18.85,15.4
12.8023,0.0,18.1,0,0.74,5.854,96.6,1.8956,24,666.0,20.2,240.52,23.79,10.8
10.6718,0.0,18.1,0,0.74,6.459,94.8,1.9879,24,666.0,20.2,43.06,23.98,11.8
6.28807,0.0,18.1,0,0.74,6.341,96.4,2.072,24,666.0,20.2,318.01,17.79,14.9
9.92485,0.0,18.1,0,0.74,6.251,96.6,2.198,24,666.0,20.2,388.52,16.44,12.6
9.32909,0.0,18.1,0,0.713,6.185,98.7,2.2616,24,666.0,20.2,396.9,18.13,14.1
7.52601,0.0,18.1,0,0.713,6.417,98.3,2.185,24,666.0,20.2,304.21,19.31,13.0
6.71772,0.0,18.1,0,0.713,6.749,92.6,2.3236,24,666.0,20.2,0.32,17.44,13.4
5.44114,0.0,18.1,0,0.713,6.655,98.2,2.3552,24,666.0,20.2,355.29,17.73,15.2
5.09017,0.0,18.1,0,0.713,6.297,91.8,2.3682,24,666.0,20.2,385.09,17.27,16.1
8.24809,0.0,18.1,0,0.713,7.393,99.3,2.4527,24,666.0,20.2,375.87,16.74,17.8
9.51363,0.0,18.1,0,0.713,6.728,94.1,2.4961,24,666.0,20.2,6.68,18.71,14.9
4.75237,0.0,18.1,0,0.713,6.525,86.5,2.4358,24,666.0,20.2,50.92,18.13,14.1
4.66883,0.0,18.1,0,0.713,5.976,87.9,2.5806,24,666.0,20.2,10.48,19.01,12.7
8.20058,0.0,18.1,0,0.713,5.936,80.3,2.7792,24,666.0,20.2,3.5,16.94,13.5
7.75223,0.0,18.1,0,0.713,6.301,83.7,2.7831,24,666.0,20.2,272.21,16.23,14.9
6.80117,0.0,18.1,0,0.713,6.081,84.4,2.7175,24,666.0,20.2,396.9,14.7,20.0
4.81213,0.0,18.1,0,0.713,6.701,90.0,2.5975,24,666.0,20.2,255.23,16.42,16.4
3.69311,0.0,18.1,0,0.713,6.376,88.4,2.5671,24,666.0,20.2,391.43,14.65,17.7
6.65492,0.0,18.1,0,0.713,6.317,83.0,2.7344,24,666.0,20.2,396.9,13.99,19.5
5.82115,0.0,18.1,0,0.713,6.513,89.9,2.8016,24,666.0,20.2,393.82,10.29,20.2
7.83932,0.0,18.1,0,0.655,6.209,65.4,2.9634,24,666.0,20.2,396.9,13.22,21.4
3.1636,0.0,18.1,0,0.655,5.759,48.2,3.0665,24,666.0,20.2,334.4,14.13,19.9
3.77498,0.0,18.1,0,0.655,5.952,84.7,2.8715,24,666.0,20.2,22.01,17.15,19.0
4.42228,0.0,18.1,0,0.584,6.003,94.5,2.5403,24,666.0,20.2,331.29,21.32,19.1
15.5757,0.0,18.1,0,0.58,5.926,71.0,2.9084,24,666.0,20.2,368.74,18.13,19.1
13.0751,0.0,18.1,0,0.58,5.713,56.7,2.8237,24,666.0,20.2,396.9,14.76,20.1
4.34879,0.0,18.1,0,0.58,6.167,84.0,3.0334,24,666.0,20.2,396.9,16.29,19.9
4.03841,0.0,18.1,0,0.532,6.229,90.7,3.0993,24,666.0,20.2,395.33,12.87,19.6
3.56868,0.0,18.1,0,0.58,6.437,75.0,2.8965,24,666.0,20.2,393.37,14.36,23.2
4.64689,0.0,18.1,0,0.614,6.98,67.6,2.5329,24,666.0,20.2,374.68,11.66,29.8
8.05579,0.0,18.1,0,0.584,5.427,95.4,2.4298,24,666.0,20.2,352.58,18.14,13.8
6.39312,0.0,18.1,0,0.584,6.162,97.4,2.206,24,666.0,20.2,302.76,24.1,13.3
4.87141,0.0,18.1,0,0.614,6.484,93.6,2.3053,24,666.0,20.2,396.21,18.68,16.7
15.0234,0.0,18.1,0,0.614,5.304,97.3,2.1007,24,666.0,20.2,349.48,24.91,12.0
10.233,0.0,18.1,0,0.614,6.185,96.7,2.1705,24,666.0,20.2,379.7,18.03,14.6
14.3337,0.0,18.1,0,0.614,6.229,88.0,1.9512,24,666.0,20.2,383.32,13.11,21.4
5.82401,0.0,18.1,0,0.532,6.242,64.7,3.4242,24,666.0,20.2,396.9,10.74,23.0
5.70818,0.0,18.1,0,0.532,6.75,74.9,3.3317,24,666.0,20.2,393.07,7.74,23.7
5.73116,0.0,18.1,0,0.532,7.061,77.0,3.4106,24,666.0,20.2,395.28,7.01,25.0
2.81838,0.0,18.1,0,0.532,5.762,40.3,4.0983,24,666.0,20.2,392.92,10.42,21.8
2.37857,0.0,18.1,0,0.583,5.871,41.9,3.724,24,666.0,20.2,370.73,13.34,20.6
3.67367,0.0,18.1,0,0.583,6.312,51.9,3.9917,24,666.0,20.2,388.62,10.58,21.2
5.69175,0.0,18.1,0,0.583,6.114,79.8,3.5459,24,666.0,20.2,392.68,14.98,19.1
4.83567,0.0,18.1,0,0.583,5.905,53.2,3.1523,24,666.0,20.2,388.22,11.45,20.6
0.15086,0.0,27.74,0,0.609,5.454,92.7,1.8209,4,711.0,20.1,395.09,18.06,15.2
0.18337,0.0,27.74,0,0.609,5.414,98.3,1.7554,4,711.0,20.1,344.05,23.97,7.0
0.20746,0.0,27.74,0,0.609,5.093,98.0,1.8226,4,711.0,20.1,318.43,29.68,8.1
0.10574,0.0,27.74,0,0.609,5.983,98.8,1.8681,4,711.0,20.1,390.11,18.07,13.6
0.11132,0.0,27.74,0,0.609,5.983,83.5,2.1099,4,711.0,20.1,396.9,13.35,20.1
0.17331,0.0,9.69,0,0.585,5.707,54.0,2.3817,6,391.0,19.2,396.9,12.01,21.8
0.27957,0.0,9.69,0,0.585,5.926,42.6,2.3817,6,391.0,19.2,396.9,13.59,24.5
0.17899,0.0,9.69,0,0.585,5.67,28.8,2.7986,6,391.0,19.2,393.29,17.6,23.1
0.2896,0.0,9.69,0,0.585,5.39,72.9,2.7986,6,391.0,19.2,396.9,21.14,19.7
0.26838,0.0,9.69,0,0.585,5.794,70.6,2.8927,6,391.0,19.2,396.9,14.1,18.3
0.23912,0.0,9.69,0,0.585,6.019,65.3,2.4091,6,391.0,19.2,396.9,12.92,21.2
0.17783,0.0,9.69,0,0.585,5.569,73.5,2.3999,6,391.0,19.2,395.77,15.1,17.5
0.22438,0.0,9.69,0,0.585,6.027,79.7,2.4982,6,391.0,19.2,396.9,14.33,16.8
0.06263,0.0,11.93,0,0.573,6.593,69.1,2.4786,1,273.0,21.0,391.99,9.67,22.4
0.04527,0.0,11.93,0,0.573,6.12,76.7,2.2875,1,273.0,21.0,396.9,9.08,20.6
0.06076,0.0,11.93,0,0.573,6.976,91.0,2.1675,1,273.0,21.0,396.9,5.64,23.9
0.10959,0.0,11.93,0,0.573,6.794,89.3,2.3889,1,273.0,21.0,393.45,6.48,22.0
0.04741,0.0,11.93,0,0.573,6.03,80.8,2.505,1,273.0,21.0,396.9,7.88,11.9


--- examples/data/mnist_test_small.csv ---

label,1x1,1x2,1x3,1x4,1x5,1x6,1x7,1x8,1x9,1x10,1x11,1x12,1x13,1x14,1x15,1x16,1x17,1x18,1x19,1x20,1x21,1x22,1x23,1x24,1x25,1x26,1x27,1x28,2x1,2x2,2x3,2x4,2x5,2x6,2x7,2x8,2x9,2x10,2x11,2x12,2x13,2x14,2x15,2x16,2x17,2x18,2x19,2x20,2x21,2x22,2x23,2x24,2x25,2x26,2x27,2x28,3x1,3x2,3x3,3x4,3x5,3x6,3x7,3x8,3x9,3x10,3x11,3x12,3x13,3x14,3x15,3x16,3x17,3x18,3x19,3x20,3x21,3x22,3x23,3x24,3x25,3x26,3x27,3x28,4x1,4x2,4x3,4x4,4x5,4x6,4x7,4x8,4x9,4x10,4x11,4x12,4x13,4x14,4x15,4x16,4x17,4x18,4x19,4x20,4x21,4x22,4x23,4x24,4x25,4x26,4x27,4x28,5x1,5x2,5x3,5x4,5x5,5x6,5x7,5x8,5x9,5x10,5x11,5x12,5x13,5x14,5x15,5x16,5x17,5x18,5x19,5x20,5x21,5x22,5x23,5x24,5x25,5x26,5x27,5x28,6x1,6x2,6x3,6x4,6x5,6x6,6x7,6x8,6x9,6x10,6x11,6x12,6x13,6x14,6x15,6x16,6x17,6x18,6x19,6x20,6x21,6x22,6x23,6x24,6x25,6x26,6x27,6x28,7x1,7x2,7x3,7x4,7x5,7x6,7x7,7x8,7x9,7x10,7x11,7x12,7x13,7x14,7x15,7x16,7x17,7x18,7x19,7x20,7x21,7x22,7x23,7x24,7x25,7x26,7x27,7x28,8x1,8x2,8x3,8x4,8x5,8x6,8x7,8x8,8x9,8x10,8x11,8x12,8x13,8x14,8x15,8x16,8x17,8x18,8x19,8x20,8x21,8x22,8x23,8x24,8x25,8x26,8x27,8x28,9x1,9x2,9x3,9x4,9x5,9x6,9x7,9x8,9x9,9x10,9x11,9x12,9x13,9x14,9x15,9x16,9x17,9x18,9x19,9x20,9x21,9x22,9x23,9x24,9x25,9x26,9x27,9x28,10x1,10x2,10x3,10x4,10x5,10x6,10x7,10x8,10x9,10x10,10x11,10x12,10x13,10x14,10x15,10x16,10x17,10x18,10x19,10x20,10x21,10x22,10x23,10x24,10x25,10x26,10x27,10x28,11x1,11x2,11x3,11x4,11x5,11x6,11x7,11x8,11x9,11x10,11x11,11x12,11x13,11x14,11x15,11x16,11x17,11x18,11x19,11x20,11x21,11x22,11x23,11x24,11x25,11x26,11x27,11x28,12x1,12x2,12x3,12x4,12x5,12x6,12x7,12x8,12x9,12x10,12x11,12x12,12x13,12x14,12x15,12x16,12x17,12x18,12x19,12x20,12x21,12x22,12x23,12x24,12x25,12x26,12x27,12x28,13x1,13x2,13x3,13x4,13x5,13x6,13x7,13x8,13x9,13x10,13x11,13x12,13x13,13x14,13x15,13x16,13x17,13x18,13x19,13x20,13x21,13x22,13x23,13x24,13x25,13x26,13x27,13x28,14x1,14x2,14x3,14x4,14x5,14x6,14x7,14x8,14x9,14x10,14x11,14x12,14x13,14x14,14x15,14x16,14x17,14x18,14x19,14x20,14x21,14x22,14x23,14x24,14x25,14x26,14x27,14x28,15x1,15x2,15x3,15x4,15x5,15x6,15x7,15x8,15x9,15x10,15x11,15x12,15x13,15x14,15x15,15x16,15x17,15x18,15x19,15x20,15x21,15x22,15x23,15x24,15x25,15x26,15x27,15x28,16x1,16x2,16x3,16x4,16x5,16x6,16x7,16x8,16x9,16x10,16x11,16x12,16x13,16x14,16x15,16x16,16x17,16x18,16x19,16x20,16x21,16x22,16x23,16x24,16x25,16x26,16x27,16x28,17x1,17x2,17x3,17x4,17x5,17x6,17x7,17x8,17x9,17x10,17x11,17x12,17x13,17x14,17x15,17x16,17x17,17x18,17x19,17x20,17x21,17x22,17x23,17x24,17x25,17x26,17x27,17x28,18x1,18x2,18x3,18x4,18x5,18x6,18x7,18x8,18x9,18x10,18x11,18x12,18x13,18x14,18x15,18x16,18x17,18x18,18x19,18x20,18x21,18x22,18x23,18x24,18x25,18x26,18x27,18x28,19x1,19x2,19x3,19x4,19x5,19x6,19x7,19x8,19x9,19x10,19x11,19x12,19x13,19x14,19x15,19x16,19x17,19x18,19x19,19x20,19x21,19x22,19x23,19x24,19x25,19x26,19x27,19x28,20x1,20x2,20x3,20x4,20x5,20x6,20x7,20x8,20x9,20x10,20x11,20x12,20x13,20x14,20x15,20x16,20x17,20x18,20x19,20x20,20x21,20x22,20x23,20x24,20x25,20x26,20x27,20x28,21x1,21x2,21x3,21x4,21x5,21x6,21x7,21x8,21x9,21x10,21x11,21x12,21x13,21x14,21x15,21x16,21x17,21x18,21x19,21x20,21x21,21x22,21x23,21x24,21x25,21x26,21x27,21x28,22x1,22x2,22x3,22x4,22x5,22x6,22x7,22x8,22x9,22x10,22x11,22x12,22x13,22x14,22x15,22x16,22x17,22x18,22x19,22x20,22x21,22x22,22x23,22x24,22x25,22x26,22x27,22x28,23x1,23x2,23x3,23x4,23x5,23x6,23x7,23x8,23x9,23x10,23x11,23x12,23x13,23x14,23x15,23x16,23x17,23x18,23x19,23x20,23x21,23x22,23x23,23x24,23x25,23x26,23x27,23x28,24x1,24x2,24x3,24x4,24x5,24x6,24x7,24x8,24x9,24x10,24x11,24x12,24x13,24x14,24x15,24x16,24x17,24x18,24x19,24x20,24x21,24x22,24x23,24x24,24x25,24x26,24x27,24x28,25x1,25x2,25x3,25x4,25x5,25x6,25x7,25x8,25x9,25x10,25x11,25x12,25x13,25x14,25x15,25x16,25x17,25x18,25x19,25x20,25x21,25x22,25x23,25x24,25x25,25x26,25x27,25x28,26x1,26x2,26x3,26x4,26x5,26x6,26x7,26x8,26x9,26x10,26x11,26x12,26x13,26x14,26x15,26x16,26x17,26x18,26x19,26x20,26x21,26x22,26x23,26x24,26x25,26x26,26x27,26x28,27x1,27x2,27x3,27x4,27x5,27x6,27x7,27x8,27x9,27x10,27x11,27x12,27x13,27x14,27x15,27x16,27x17,27x18,27x19,27x20,27x21,27x22,27x23,27x24,27x25,27x26,27x27,27x28,28x1,28x2,28x3,28x4,28x5,28x6,28x7,28x8,28x9,28x10,28x11,28x12,28x13,28x14,28x15,28x16,28x17,28x18,28x19,28x20,28x21,28x22,28x23,28x24,28x25,28x26,28x27,28x28
7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,192,130,253,255,253,244,105,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,208,208,208,231,252,252,252,253,252,252,252,213,61,0,0,0,0,0,0,0,0,0,0,0,0,0,0,164,252,252,252,252,252,252,252,238,237,237,252,252,171,7,0,0,0,0,0,0,0,0,0,0,0,0,0,94,252,252,252,252,139,132,132,0,0,8,189,252,252,118,0,0,0,0,0,0,0,0,0,0,0,0,0,2,99,199,252,252,14,0,0,0,0,0,120,252,252,236,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,222,222,12,0,0,0,0,0,120,252,252,252,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,252,252,252,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,166,252,252,204,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,145,252,252,246,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,252,252,252,133,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,227,253,253,222,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,227,252,252,223,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,216,253,252,242,125,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,221,252,253,223,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,233,252,252,222,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,48,217,252,252,252,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,252,252,252,252,172,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,160,250,252,252,252,69,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,252,252,252,228,154,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,103,235,234,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,170,226,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,170,255,141,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,170,255,170,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,170,255,57,0,0,0,198,255,141,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,170,255,170,29,0,0,170,255,198,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,170,255,198,0,0,0,114,255,226,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,255,226,29,0,0,57,255,226,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,255,226,57,0,0,29,226,255,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,226,255,198,86,86,86,198,255,226,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,226,255,255,255,255,255,255,198,141,57,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,86,86,86,226,255,170,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,255,198,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,255,226,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,226,255,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,255,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,255,141,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,255,198,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,226,57,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,255,141,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,226,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,214,253,189,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,131,253,251,251,188,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,251,253,251,251,188,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,202,251,253,243,188,42,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,158,240,251,193,82,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,253,253,253,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,92,236,251,251,231,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,253,251,251,140,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,240,253,251,219,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,181,251,253,231,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,253,253,219,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,221,251,251,138,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,221,251,251,172,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,134,251,251,235,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,217,253,251,172,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,124,253,255,241,79,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,112,244,251,241,93,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,251,251,251,79,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,251,196,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,228,251,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,132,248,255,253,253,253,138,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,123,237,252,252,253,252,252,252,252,221,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,184,252,252,252,217,96,96,96,176,224,252,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,229,252,252,164,84,4,0,0,0,0,20,175,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,169,252,235,94,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,209,252,88,0,0,0,0,0,0,0,0,53,73,49,0,0,0,0,0,0,0,0,0,0,0,0,0,30,237,252,60,0,0,0,0,0,9,85,165,239,252,167,0,0,0,0,0,0,0,0,0,0,0,0,0,6,180,252,191,97,60,0,93,103,220,252,252,252,252,122,0,0,0,0,0,0,0,0,0,0,0,0,0,0,118,252,252,252,243,229,251,253,252,252,252,252,221,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,40,131,189,131,131,131,11,35,252,252,194,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,202,253,164,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,81,202,252,249,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,243,253,237,82,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,194,252,228,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,183,252,252,92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,244,252,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,243,252,124,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,53,244,252,124,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,200,252,124,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,163,252,183,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,32,112,190,91,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,127,174,251,251,251,251,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,64,210,251,253,251,251,235,188,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,131,251,251,251,253,231,94,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,255,253,253,253,253,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,170,253,251,251,204,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,221,251,253,251,188,94,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,221,248,251,229,168,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,228,251,251,251,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,255,253,253,229,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,228,253,251,235,66,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,158,251,253,156,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,158,251,251,229,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,131,251,251,251,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,253,253,229,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,115,251,235,66,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,235,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,247,254,255,123,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,246,253,253,203,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,203,253,253,253,215,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,195,253,253,253,138,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,124,248,253,253,158,41,0,0,0,0,0,0,0,0,0,0,39,47,0,0,0,0,0,0,0,0,0,0,0,143,253,253,253,244,77,0,0,0,0,0,0,0,0,0,16,20,0,0,0,0,0,0,0,0,0,0,120,212,253,253,253,253,156,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,106,192,253,253,253,253,253,156,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,70,229,253,253,251,249,253,253,253,156,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,208,253,253,253,201,130,131,253,253,253,156,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,133,202,253,253,253,91,36,0,131,253,253,253,156,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,216,253,253,228,67,1,0,0,96,241,253,253,156,0,0,0,0,0,0,0,0,0,0,0,0,0,26,199,253,253,248,68,0,0,0,0,0,211,253,253,156,0,0,0,0,0,0,0,0,0,0,0,0,0,202,253,253,231,72,0,0,0,0,0,120,249,253,253,156,0,0,0,0,0,0,0,0,0,0,0,0,0,219,253,253,147,0,0,0,0,0,0,131,253,253,253,156,0,0,0,0,0,0,0,0,0,0,0,0,0,219,253,253,147,0,0,0,0,0,8,148,253,253,238,27,0,0,0,0,0,0,0,0,0,0,0,0,0,219,253,253,195,31,0,0,112,115,143,253,253,201,129,0,0,0,0,0,0,0,0,0,0,0,0,0,0,207,253,253,253,209,193,193,251,253,253,253,214,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,203,253,253,253,253,253,253,253,253,202,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,217,253,253,253,253,204,78,78,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,191,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,128,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,191,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,191,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,128,0,0,64,128,255,191,128,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,128,191,255,255,255,255,255,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,255,255,255,128,191,128,128,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,255,128,64,0,0,64,128,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,255,255,128,128,255,255,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,255,255,255,255,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,191,255,191,128,128,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,95,218,248,252,252,248,248,155,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,251,253,253,253,253,253,253,252,155,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,250,253,253,253,253,253,253,253,253,234,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,212,249,253,253,253,253,253,253,253,253,253,234,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,221,253,253,253,180,185,185,228,253,253,253,253,241,40,0,0,0,0,0,0,0,0,0,0,0,0,38,221,253,253,225,128,8,10,10,139,253,253,253,253,253,225,39,0,0,0,0,0,0,0,0,0,0,0,219,253,253,184,117,0,0,0,0,10,135,227,253,253,253,253,224,0,0,0,0,0,0,0,0,0,0,95,251,253,138,7,0,0,0,0,0,0,0,35,135,226,253,253,249,35,0,0,0,0,0,0,0,0,33,220,253,172,6,0,0,0,0,0,0,0,0,0,0,36,135,225,253,224,0,0,0,0,0,0,0,0,125,253,253,119,0,0,0,0,0,0,0,0,0,0,0,0,0,143,253,223,0,0,0,0,0,0,0,0,218,253,253,46,0,0,0,0,0,0,0,0,0,0,0,0,0,143,253,123,0,0,0,0,0,0,0,0,255,253,210,55,0,0,0,0,0,0,0,0,0,0,0,5,56,220,249,38,0,0,0,0,0,0,0,0,254,253,100,0,0,0,0,0,0,0,0,0,0,0,6,62,253,253,226,0,0,0,0,0,0,0,0,0,238,253,214,12,0,0,0,0,0,0,0,0,0,22,128,253,253,161,42,0,0,0,0,0,0,0,0,0,112,253,253,100,0,0,0,0,0,0,0,6,50,221,253,250,150,42,0,0,0,0,0,0,0,0,0,0,0,170,253,211,100,20,20,20,20,54,150,171,253,253,224,93,0,0,0,0,0,0,0,0,0,0,0,0,0,51,240,253,253,253,253,253,253,253,253,253,251,220,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,170,247,249,253,253,251,248,248,152,96,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45,123,123,89,32,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,255,253,57,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,224,253,251,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,253,254,253,226,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,251,253,251,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,197,254,253,254,253,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,251,253,251,253,138,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,253,254,253,169,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197,251,253,251,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,253,254,253,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,251,253,138,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,254,253,169,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,253,251,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,253,254,253,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,251,253,251,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,253,254,253,169,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,251,253,251,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,169,254,253,254,253,169,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,224,253,251,253,251,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,253,254,253,254,253,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,251,253,251,253,251,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,118,121,254,254,255,221,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,79,201,253,253,253,253,253,253,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,206,253,253,253,253,253,253,253,247,81,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,216,253,253,253,253,253,253,253,253,96,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,253,253,253,253,253,253,253,253,253,96,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,214,247,239,239,239,109,151,253,253,96,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,67,0,75,67,0,83,253,253,96,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,82,74,0,83,253,213,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,131,253,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,229,253,75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,204,253,208,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,241,253,176,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,75,248,206,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,75,253,253,170,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,104,104,104,104,104,104,104,189,253,253,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,111,220,243,253,253,253,253,253,253,253,253,253,197,16,0,0,0,0,0,0,0,0,0,0,0,0,0,184,245,253,253,253,253,226,191,208,253,253,253,253,253,161,14,0,0,0,0,0,0,0,0,0,0,0,20,237,253,253,248,212,212,46,0,92,253,253,230,232,253,253,194,53,0,0,0,0,0,0,0,0,0,0,118,253,253,249,127,0,0,9,70,236,253,225,41,47,226,250,253,186,71,0,0,0,0,0,0,0,0,0,118,253,253,198,0,0,0,185,253,253,227,41,0,0,0,145,253,253,244,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,255,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,217,253,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,89,254,234,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,163,242,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,99,254,164,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,210,228,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,173,253,140,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,201,75,0,0,0,0,29,253,253,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,217,254,128,0,0,0,0,42,254,207,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,129,253,171,9,0,0,0,0,166,253,93,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,254,209,13,0,0,0,0,89,254,234,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,238,242,59,0,0,0,0,0,163,242,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,205,254,243,141,141,141,142,66,154,254,215,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,253,253,254,253,253,253,254,253,253,253,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,168,168,169,168,168,119,128,253,253,165,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,253,215,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,205,254,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,76,254,190,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,151,254,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,238,229,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,233,253,253,255,253,133,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,73,155,233,252,252,252,253,252,215,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,182,201,252,252,253,252,252,252,253,252,195,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,253,252,252,252,253,168,231,108,108,108,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,109,191,253,252,236,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,190,252,253,252,174,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,191,252,253,252,180,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,252,252,253,252,252,190,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,110,253,253,253,255,253,253,253,192,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,159,158,35,35,35,66,252,253,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,175,253,231,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,144,253,252,71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,84,255,253,72,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,252,113,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,252,215,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,84,253,252,132,0,0,0,0,0,0,0,0,0,0,0,0,0,0,94,109,47,0,0,0,0,0,0,16,191,255,222,41,0,0,0,0,0,0,0,0,0,0,0,0,0,0,175,252,232,217,217,114,73,218,217,222,252,253,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,92,215,252,252,252,252,253,252,252,231,154,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,108,108,108,190,253,128,108,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,191,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,191,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,64,0,0,128,128,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,191,0,128,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,0,128,255,255,255,255,255,255,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,64,255,255,255,255,255,255,255,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,128,255,255,255,128,128,128,255,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,191,255,255,0,0,0,0,191,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,255,255,128,128,191,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,255,255,255,255,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,191,255,255,255,255,191,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,192,113,25,29,253,141,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,140,253,252,231,228,252,140,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,165,253,252,252,252,252,166,57,57,57,57,57,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,252,253,252,252,252,252,253,252,252,252,252,253,106,0,0,0,0,0,0,0,0,0,0,0,0,0,0,107,252,253,252,252,252,252,253,252,252,252,252,190,12,0,0,0,0,0,0,0,0,0,0,0,0,0,26,207,253,255,253,165,140,140,141,241,253,240,140,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,252,252,215,42,6,0,0,101,249,252,176,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,71,195,110,0,0,7,57,222,252,98,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,252,253,242,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,252,253,167,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,169,253,255,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,98,243,252,152,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197,252,252,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,73,234,249,145,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,240,252,223,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,253,228,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,159,252,148,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,253,252,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,215,253,195,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,189,253,167,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,255,161,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,118,118,206,253,253,161,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,212,244,253,253,253,253,253,252,105,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,87,214,253,253,178,167,253,240,82,149,65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,209,249,253,177,58,7,97,253,170,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,217,253,253,190,8,0,0,176,219,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,99,253,253,227,54,0,0,45,242,214,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,99,253,253,68,0,0,19,181,253,107,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,244,253,148,0,0,96,253,240,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,165,243,235,196,128,239,253,174,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,215,253,253,253,253,214,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,148,253,253,253,253,174,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,93,242,253,253,253,223,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,189,253,253,253,253,253,253,71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,122,253,253,253,253,253,253,253,71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,235,253,253,253,253,253,253,217,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,246,253,246,206,230,253,248,113,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,253,253,214,237,253,253,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,253,253,253,253,251,210,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,54,244,253,253,142,85,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,149,228,254,254,210,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,112,217,254,253,253,253,253,218,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,102,190,253,253,233,197,127,153,232,254,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,219,253,253,184,84,0,0,0,0,0,217,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,54,229,254,173,95,14,0,0,36,53,0,0,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,184,254,89,0,0,0,0,8,202,233,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,253,253,0,0,0,0,72,202,253,152,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,246,253,22,102,128,198,250,254,253,74,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,143,253,254,253,253,237,216,255,232,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,147,148,147,138,32,150,254,81,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65,254,213,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,253,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,212,253,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,93,253,182,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,216,200,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,147,254,190,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,212,253,190,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,193,253,252,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,253,161,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,89,254,173,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,53,128,254,255,254,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,158,220,253,253,253,253,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,190,253,253,253,253,253,253,215,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,187,253,253,253,253,253,253,253,253,202,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,44,187,253,253,253,253,253,253,253,253,253,253,91,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,253,253,253,253,253,253,253,253,253,253,253,253,235,0,0,0,0,0,0,0,0,0,0,0,0,0,15,187,253,253,253,253,244,226,252,237,69,52,219,253,235,0,0,0,0,0,0,0,0,0,0,0,0,26,187,253,253,253,253,249,97,0,143,61,0,0,211,253,235,0,0,0,0,0,0,0,0,0,0,0,0,62,253,253,248,244,244,114,0,0,0,0,0,0,211,253,248,115,0,0,0,0,0,0,0,0,0,0,0,123,253,237,85,0,0,0,0,0,0,0,0,0,211,253,253,156,0,0,0,0,0,0,0,0,0,0,0,237,253,209,0,0,0,0,0,0,0,0,0,0,211,253,253,156,0,0,0,0,0,0,0,0,0,0,153,252,253,209,0,0,0,0,0,0,0,0,0,0,211,253,248,114,0,0,0,0,0,0,0,0,0,0,158,253,253,209,0,0,0,0,0,0,0,0,0,0,211,253,235,0,0,0,0,0,0,0,0,0,0,0,158,253,253,209,0,0,0,0,0,0,0,0,0,28,219,253,199,0,0,0,0,0,0,0,0,0,0,0,158,253,253,209,0,0,0,0,0,0,0,0,28,195,253,253,60,0,0,0,0,0,0,0,0,0,0,0,27,240,253,209,0,0,0,0,0,0,35,138,219,253,223,138,10,0,0,0,0,0,0,0,0,0,0,0,0,237,253,229,115,115,67,4,115,115,249,253,253,253,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,104,253,253,253,253,228,195,253,253,253,253,200,60,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,156,240,253,253,253,253,253,253,226,156,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,98,253,253,253,253,192,78,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,67,194,244,106,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,242,254,254,207,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,161,254,254,254,207,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,161,254,254,254,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,161,254,254,254,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,79,238,254,247,109,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,254,254,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,190,254,254,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,254,254,254,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,218,254,254,254,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,218,254,254,230,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,245,254,254,188,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,233,254,188,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,58,221,254,254,94,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,118,254,254,254,94,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,254,254,254,254,94,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,254,254,254,254,207,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,203,254,254,254,254,99,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,209,254,254,241,131,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,107,243,254,254,85,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,53,101,101,175,254,254,255,124,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,99,200,228,253,253,253,253,253,253,253,213,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,205,253,253,253,253,253,253,253,154,173,253,250,85,0,0,0,0,0,0,0,0,0,0,0,0,0,34,215,253,253,253,253,253,253,253,35,3,3,217,253,145,0,0,0,0,0,0,0,0,0,0,0,0,0,210,253,253,253,253,253,253,118,61,7,0,18,222,253,145,0,0,0,0,0,0,0,0,0,0,0,0,0,162,253,253,253,253,181,115,35,0,0,9,191,253,249,66,0,0,0,0,0,0,0,0,0,0,0,0,0,6,131,168,117,14,7,0,0,0,0,16,253,253,193,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,138,253,208,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,149,253,253,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,224,253,218,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,99,244,242,42,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,99,253,253,162,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,70,62,0,111,244,253,234,61,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,40,215,253,243,169,246,253,253,81,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,247,253,253,253,253,253,253,253,253,131,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,126,252,253,253,253,253,253,253,253,253,253,251,216,178,23,0,0,0,0,0,0,0,0,0,0,0,0,0,53,249,253,253,253,253,249,105,106,170,227,250,253,253,195,94,0,0,0,0,0,0,0,0,0,0,0,0,0,247,253,253,253,240,70,0,0,0,0,105,253,253,253,145,0,0,0,0,0,0,0,0,0,0,0,0,0,247,253,253,150,40,0,0,0,0,0,3,45,176,199,115,0,0,0,0,0,0,0,0,0,0,0,0,0,247,229,99,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,54,143,255,254,254,254,171,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,154,241,243,253,253,253,253,253,170,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,253,253,239,177,146,253,253,170,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,198,161,50,0,144,253,253,67,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,218,253,183,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,151,218,253,253,212,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,118,230,253,253,253,253,253,170,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,167,253,253,253,253,190,197,253,206,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,227,253,253,253,161,27,12,13,242,253,54,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,184,184,184,73,13,0,0,0,241,253,92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,243,253,165,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,253,253,54,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,7,3,0,0,0,0,0,0,1,128,253,251,53,0,0,0,0,0,0,0,0,0,0,0,0,0,38,152,253,103,0,0,0,0,0,0,75,253,253,170,0,0,0,0,0,0,0,0,0,0,0,0,0,0,76,253,253,103,0,0,0,0,0,0,144,253,253,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,76,253,253,226,41,0,0,0,0,69,239,253,119,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,76,253,253,253,225,39,0,0,72,234,253,223,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,105,240,253,253,221,130,153,233,253,226,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65,178,253,253,253,253,253,251,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,116,175,253,216,116,104,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,254,111,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,154,253,110,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,249,253,110,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,154,253,235,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,244,243,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,116,222,98,0,0,17,184,253,227,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,247,253,188,0,0,46,253,242,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,215,253,253,188,0,17,177,253,180,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,226,253,159,38,0,156,253,242,65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,253,253,104,66,66,201,253,180,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,25,110,245,253,253,253,253,253,253,253,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,131,253,253,253,253,226,253,253,253,220,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,78,248,253,237,164,78,38,213,253,244,104,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,94,234,214,70,0,0,69,234,253,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,0,0,0,0,202,253,215,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,234,253,200,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,253,253,253,71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,234,253,253,250,67,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,253,253,214,90,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,253,149,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,85,141,253,254,253,57,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,168,169,168,253,251,253,251,253,251,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,253,254,253,254,253,254,253,254,253,254,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,251,253,251,253,251,253,251,196,83,84,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,253,226,168,169,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,168,253,251,225,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,253,255,253,254,253,198,85,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,251,253,251,253,251,253,251,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,198,253,226,168,169,168,254,253,254,139,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,83,56,0,0,0,139,251,253,251,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,225,254,253,169,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,253,251,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,253,254,253,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,251,253,251,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,197,198,28,0,0,0,0,0,0,85,253,254,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,251,253,83,0,0,0,0,0,0,197,251,196,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,253,254,253,0,0,0,0,141,253,254,253,169,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,251,253,251,114,0,57,168,253,251,253,251,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,198,253,254,253,254,253,254,253,226,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,83,253,251,253,251,253,138,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,191,208,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,252,238,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,73,191,252,217,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,217,252,252,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,232,252,108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,84,233,252,220,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,105,253,252,195,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,105,206,253,210,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,182,253,255,180,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,201,252,222,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,176,252,252,186,31,58,181,182,181,78,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,237,252,252,0,155,252,252,253,252,252,252,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,191,253,253,253,255,253,253,253,255,253,253,253,110,15,0,0,0,0,0,0,0,0,0,0,0,0,0,1,232,252,252,252,253,252,252,252,253,252,252,252,253,35,0,0,0,0,0,0,0,0,0,0,0,0,0,105,252,252,252,252,154,154,154,71,72,113,221,252,175,10,0,0,0,0,0,0,0,0,0,0,0,0,0,144,252,252,252,252,0,0,0,0,0,0,120,252,144,0,0,0,0,0,0,0,0,0,0,0,0,0,16,191,253,253,253,108,0,94,109,109,110,233,253,191,84,0,0,0,0,0,0,0,0,0,0,0,0,0,5,119,252,252,252,232,218,247,252,252,253,252,241,159,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,149,252,252,252,253,252,252,252,217,174,61,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,108,108,190,253,210,108,108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,253,255,253,255,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,253,251,253,251,253,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,169,255,253,254,253,254,253,254,253,254,139,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,169,224,253,251,253,251,253,251,253,251,253,251,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,169,255,253,255,253,254,253,254,253,254,253,254,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,224,253,251,253,251,84,196,253,251,253,251,253,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,225,255,253,169,56,0,0,85,253,254,253,169,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,253,251,0,0,0,0,197,251,253,251,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,197,254,253,254,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197,251,253,251,196,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,254,253,254,139,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,251,253,251,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,225,254,253,169,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,169,224,253,138,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,254,253,254,139,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,253,251,196,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,169,254,253,226,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,169,224,253,138,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,254,253,254,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,139,251,196,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,121,254,254,135,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,97,197,236,253,253,253,253,238,69,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,135,227,253,253,253,210,177,208,253,253,75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,157,253,253,238,198,109,27,0,104,253,253,75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,108,253,253,174,60,0,0,0,0,123,253,192,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,151,253,253,27,0,0,2,14,82,248,253,54,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,204,253,253,27,0,0,99,253,253,253,210,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,209,253,253,126,147,248,251,253,253,208,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,186,253,253,253,253,253,202,97,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,253,253,253,253,253,193,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,226,253,253,253,253,253,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,59,253,253,253,253,253,253,211,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,119,253,253,253,253,201,228,253,163,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,222,253,253,253,132,103,253,253,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,91,220,253,191,150,4,41,228,253,163,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,94,253,253,103,0,0,90,253,253,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,253,226,22,0,0,198,253,215,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,76,253,243,171,42,176,245,242,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,140,253,253,253,253,253,210,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,89,134,253,253,253,130,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,128,253,255,253,253,140,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,128,241,252,252,253,252,252,252,249,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,103,252,252,252,252,209,198,252,252,252,66,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,207,252,187,39,39,21,17,69,231,252,66,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,67,252,252,93,0,0,32,193,219,252,252,66,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,67,252,252,127,61,186,219,252,252,252,174,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,67,252,252,252,252,252,234,250,252,197,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,25,121,225,225,225,154,126,247,252,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,121,252,252,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,190,252,202,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,121,255,253,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,202,253,195,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,177,253,212,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,252,253,104,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,147,252,221,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,126,252,252,133,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,160,252,252,113,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,169,252,252,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,252,252,174,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,252,245,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,17,17,92,180,96,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,78,253,253,253,253,253,220,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,87,235,253,253,253,253,253,253,106,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,33,203,253,253,253,253,253,253,253,253,106,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,213,224,253,253,253,253,253,253,253,253,253,106,0,0,0,0,0,0,0,0,0,0,0,0,0,0,69,161,253,253,253,253,253,253,253,253,253,253,253,187,99,0,0,0,0,0,0,0,0,0,0,0,0,73,241,253,253,253,253,199,201,253,253,253,253,253,253,253,246,81,0,0,0,0,0,0,0,0,0,0,66,231,253,253,253,112,98,147,253,253,136,98,123,253,253,253,253,89,0,0,0,0,0,0,0,0,0,17,249,253,253,205,7,1,0,3,7,7,2,0,2,7,63,253,253,249,0,0,0,0,0,0,0,0,0,128,253,252,245,91,0,0,0,0,0,0,0,0,0,0,19,181,253,253,0,0,0,0,0,0,0,0,36,209,253,220,0,0,0,0,0,0,0,0,0,0,0,0,23,189,253,253,0,0,0,0,0,0,0,0,107,253,253,98,0,0,0,0,0,0,0,0,0,0,0,13,77,253,253,130,0,0,0,0,0,0,0,0,254,253,253,56,0,0,0,0,0,0,0,0,0,0,19,186,253,253,219,49,0,0,0,0,0,0,0,0,255,253,183,20,0,0,0,0,0,0,0,17,42,116,213,253,253,229,44,0,0,0,0,0,0,0,0,0,254,253,253,158,131,131,131,131,131,131,131,179,253,253,253,253,137,51,0,0,0,0,0,0,0,0,0,0,217,253,253,253,253,253,253,253,253,253,253,253,247,195,169,32,4,0,0,0,0,0,0,0,0,0,0,0,38,115,253,253,253,253,253,253,253,253,248,106,96,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,15,143,179,179,179,30,15,15,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,237,255,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,149,253,253,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,246,253,253,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,253,253,253,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,246,253,253,253,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,82,249,253,253,189,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,167,253,253,211,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,202,253,253,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,94,253,253,253,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,253,253,253,201,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,253,253,253,104,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,179,253,253,233,75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,149,225,253,253,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,253,253,253,218,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,118,253,253,253,87,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,209,253,253,253,87,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,181,253,253,253,213,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,253,253,253,246,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,253,253,253,244,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,253,253,253,123,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,85,86,197,254,139,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,253,251,253,251,253,251,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,255,253,255,253,255,253,254,253,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,253,251,253,251,253,251,253,251,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,253,255,253,255,253,255,253,254,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197,251,253,138,84,196,253,251,253,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,114,0,0,0,255,253,226,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,253,251,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,197,254,253,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197,251,253,251,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,253,254,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,251,196,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,197,255,253,254,253,85,197,254,253,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,253,251,253,251,253,251,253,251,253,251,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,253,255,253,255,253,254,253,254,253,254,253,198,85,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,251,253,251,253,251,253,251,253,251,253,251,253,251,114,0,0,0,0,0,0,0,0,0,0,0,0,0,198,253,254,253,141,253,254,253,254,196,57,168,254,253,254,84,0,0,0,0,0,0,0,0,0,0,0,0,28,196,253,251,253,251,253,251,84,28,0,0,253,251,253,83,0,0,0,0,0,0,0,0,0,0,0,0,0,169,254,253,254,253,169,168,0,0,0,0,57,168,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,253,251,84,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,85,255,253,255,253,255,253,198,85,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197,251,253,251,253,251,253,251,253,251,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,254,253,254,253,254,253,254,253,254,253,254,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,251,253,251,253,138,253,251,253,251,253,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,254,253,254,253,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,169,224,253,251,253,138,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,85,197,254,253,254,253,254,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,169,224,253,251,253,251,253,251,253,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,169,254,253,254,253,254,253,254,253,254,253,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,253,251,253,138,139,138,139,251,253,251,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,169,168,0,0,0,0,85,253,254,253,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,251,253,251,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,253,254,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,253,251,253,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,253,254,253,114,0,0,0,0,0,0,0,0,0,0,0,0,0,57,168,0,0,0,0,0,0,0,114,253,251,253,251,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,253,254,139,57,0,85,85,254,253,254,253,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,251,253,251,225,168,253,251,253,251,253,138,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,170,225,254,253,254,253,254,253,226,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,84,83,253,251,253,251,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,255,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,69,253,242,172,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,73,253,253,188,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,191,253,200,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,197,253,175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,131,253,253,129,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,230,253,253,106,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,183,69,0,0,0,0,0,0,85,243,253,174,54,0,0,0,0,0,0,0,0,0,0,0,0,0,101,131,246,142,0,0,0,0,0,89,187,253,169,70,0,0,0,0,0,0,0,0,0,0,0,0,0,131,248,253,175,6,0,0,0,0,64,246,253,234,64,0,0,0,0,0,0,0,0,0,0,0,0,0,14,247,253,253,176,24,24,24,22,0,70,253,253,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,146,253,253,253,253,253,253,253,241,124,159,253,253,117,0,0,0,0,0,0,0,0,0,0,0,0,0,84,241,253,253,253,253,253,253,253,253,253,253,253,253,38,0,0,0,0,0,0,0,0,0,0,0,0,68,236,253,253,253,253,187,84,84,130,239,253,253,253,175,13,0,0,0,0,0,0,0,0,0,0,0,0,101,253,253,253,253,189,8,0,0,0,94,253,253,246,75,0,0,0,0,0,0,0,0,0,0,0,0,0,138,253,253,230,74,17,0,0,0,104,242,253,253,247,84,0,0,0,0,0,0,0,0,0,0,0,0,0,152,217,92,58,0,0,0,0,85,245,253,253,253,233,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,183,253,253,253,223,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,179,253,248,199,144,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,99,240,230,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,131,225,225,225,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,73,197,253,252,252,252,252,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,29,29,154,187,252,252,253,252,252,233,145,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,252,253,252,252,252,252,253,204,112,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,169,253,255,253,228,126,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,98,243,252,253,252,246,130,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,98,240,252,252,253,252,252,252,221,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,225,252,252,236,225,223,230,252,252,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,146,252,157,50,0,0,25,205,252,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,207,253,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,19,0,0,0,0,0,0,0,0,0,73,205,252,79,0,0,0,0,0,0,0,0,0,0,0,0,120,215,209,175,0,0,0,0,0,0,0,19,209,252,220,79,0,0,0,0,0,0,0,0,0,0,0,0,174,252,252,239,140,0,0,0,0,0,29,104,252,249,177,0,0,0,0,0,0,0,0,0,0,0,0,0,174,252,252,223,0,0,0,0,0,0,174,252,252,223,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,241,253,146,0,0,0,0,169,253,255,253,253,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,178,252,154,85,85,210,225,243,252,215,121,27,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,208,220,252,253,252,252,214,195,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,37,84,146,223,114,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,25,114,254,254,74,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,25,194,253,253,235,144,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,227,253,253,214,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,183,253,253,253,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45,238,253,249,178,126,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,162,254,253,220,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,253,254,253,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,173,250,253,254,143,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,205,253,253,86,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,247,253,164,62,0,98,79,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,254,254,228,105,106,140,254,254,122,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,253,253,253,253,254,253,253,253,253,110,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,58,227,253,253,253,253,254,253,253,253,253,223,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,224,253,253,253,253,253,254,253,253,253,253,152,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,107,250,253,253,253,253,121,105,227,253,253,231,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,253,253,253,253,253,210,254,253,251,223,69,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,253,253,253,253,253,253,254,221,112,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,78,190,253,253,253,253,253,255,214,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,180,141,59,59,155,104,166,180,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,110,71,142,168,164,105,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,254,255,236,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,164,253,253,253,226,60,47,47,47,56,200,200,200,116,0,0,0,0,0,0,0,0,0,0,0,0,0,54,163,253,253,253,253,253,253,253,253,253,253,253,253,145,0,0,0,0,0,0,0,0,0,0,0,0,0,93,253,253,253,253,253,253,253,253,253,253,253,253,250,93,0,0,0,0,0,0,0,0,0,0,0,0,0,210,253,253,253,253,233,74,61,61,114,253,253,253,128,0,0,0,0,0,0,0,0,0,0,0,0,0,81,250,253,253,253,236,56,0,0,0,70,253,253,219,42,0,0,0,0,0,0,0,0,0,0,0,0,0,146,253,240,84,14,13,0,0,8,85,225,253,214,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,68,58,0,0,0,0,0,43,253,253,251,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,216,253,253,157,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,218,253,253,142,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,93,246,251,230,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,91,190,253,230,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,204,253,253,187,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,95,253,253,192,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,245,253,253,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,166,253,253,253,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,139,253,253,253,253,78,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,93,253,253,253,253,197,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,93,253,253,235,87,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,93,253,253,75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,113,255,253,253,253,174,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,210,225,228,252,253,252,252,252,252,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,135,253,252,252,214,195,196,246,252,245,118,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,138,224,252,240,223,192,28,0,13,228,252,223,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,252,252,252,63,0,0,0,0,191,252,252,145,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,147,253,178,63,0,0,0,92,253,255,253,133,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,225,252,84,0,0,76,194,243,252,253,129,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,225,252,199,57,198,246,252,252,220,56,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130,252,252,252,253,242,223,129,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,252,252,252,253,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,253,253,253,253,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,73,225,253,252,252,252,252,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,88,234,252,253,154,177,252,252,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,252,252,252,240,24,57,252,252,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,225,252,252,252,63,0,57,252,252,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,238,253,240,63,0,38,222,253,253,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,238,252,252,99,0,0,131,252,242,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,252,208,56,57,198,246,252,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,146,249,233,224,252,253,242,192,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,225,252,252,252,112,74,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,128,254,255,254,194,123,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,173,253,253,253,253,253,253,88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,179,253,253,253,208,147,225,253,88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,216,253,208,182,92,11,0,128,168,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,107,253,245,61,0,0,0,40,226,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,156,253,180,0,25,107,107,227,253,224,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,167,253,181,199,245,253,253,253,239,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,97,252,253,253,253,253,253,179,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,147,212,219,253,253,174,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,253,235,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,232,253,140,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,236,253,203,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,59,253,253,87,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,241,253,152,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,107,253,239,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,184,253,152,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65,245,252,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,183,253,247,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,207,253,247,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,207,253,137,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,118,118,121,254,254,135,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,95,220,250,253,253,253,253,253,253,54,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,111,252,253,253,253,253,253,253,253,253,54,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130,253,253,253,253,253,253,253,253,253,54,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,219,219,219,219,241,253,253,253,242,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,165,253,253,248,87,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,0,165,253,253,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,170,248,251,248,251,253,253,91,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130,253,253,253,253,253,253,253,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130,253,253,253,253,253,253,253,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130,253,253,240,179,162,253,253,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130,241,103,65,0,28,253,253,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,62,0,0,0,28,253,253,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,253,253,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,84,253,253,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,83,79,0,0,0,0,0,165,253,253,106,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,253,250,128,63,7,51,89,234,253,223,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,87,217,253,253,253,186,239,253,253,248,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,234,253,253,253,253,253,240,151,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,186,253,253,216,116,41,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,85,141,253,255,253,254,253,57,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,251,253,251,253,251,253,251,225,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,197,254,253,254,253,254,253,254,253,226,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,197,251,253,251,253,251,253,251,253,251,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,169,254,253,254,253,254,196,198,253,254,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,253,251,253,251,84,28,85,251,253,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,254,253,226,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,253,251,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,253,254,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,253,251,253,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,85,85,85,85,197,254,253,254,253,226,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,197,251,253,251,253,251,253,251,253,251,225,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,197,254,253,254,253,254,253,254,253,254,253,254,139,0,0,0,0,0,0,0,0,0,0,0,0,0,114,197,251,253,251,196,196,253,251,253,251,253,251,253,251,114,0,0,0,0,0,0,0,0,0,0,0,29,197,254,253,254,196,29,197,254,253,254,84,0,114,254,253,254,84,0,0,0,0,0,0,0,0,0,0,85,251,253,251,253,83,197,251,253,251,196,28,0,0,139,251,253,196,0,0,0,0,0,0,0,0,0,0,86,253,254,253,254,253,254,253,226,56,0,0,0,0,0,169,254,253,57,0,0,0,0,0,0,0,0,0,28,196,253,251,253,251,253,138,56,0,0,0,0,0,0,168,253,251,168,0,0,0,0,0,0,0,0,0,0,169,254,253,254,253,169,56,0,0,0,0,0,0,0,0,57,168,0,0,0,0,0,0,0,0,0,0,0,168,253,251,196,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,113,176,253,253,146,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,98,228,252,253,252,252,249,225,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,104,240,252,252,253,252,252,252,252,140,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,252,252,252,252,162,84,84,180,252,140,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,79,253,252,252,236,112,0,0,0,29,252,140,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,253,255,215,140,47,0,0,0,0,0,79,255,253,56,0,0,0,0,0,0,0,0,0,0,0,0,0,154,252,215,33,0,0,0,0,0,0,0,48,253,252,55,0,0,0,0,0,0,0,0,0,0,0,0,76,234,252,140,0,0,0,0,0,0,0,23,227,253,233,43,0,0,0,0,0,0,0,0,0,0,0,0,197,252,252,47,0,0,0,0,0,0,23,131,252,253,151,0,0,0,0,0,0,0,0,0,0,0,0,0,197,252,173,0,0,0,0,0,0,48,227,252,252,190,12,0,0,0,0,0,0,0,0,0,0,0,0,0,198,253,253,114,113,113,113,113,255,253,253,253,253,141,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197,252,252,253,252,252,252,252,253,176,233,252,252,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,155,252,253,252,208,195,195,56,25,209,252,141,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,84,84,84,19,0,0,0,101,252,233,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,225,252,223,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,253,253,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,113,252,201,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,222,252,164,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,252,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,79,253,252,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,222,253,253,255,253,253,253,253,192,75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,252,252,252,253,252,252,252,252,253,243,175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,239,252,252,253,252,252,198,55,253,252,195,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,143,87,96,84,84,84,28,95,253,252,180,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,50,0,0,0,95,203,253,252,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,144,253,253,255,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,163,226,249,252,252,252,75,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,138,252,253,252,252,252,252,166,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,169,252,240,99,84,103,252,253,122,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,75,112,63,0,0,29,252,253,167,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,253,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,126,253,252,180,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,140,253,252,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,140,253,195,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,140,253,89,0,0,0,0,0,0,0,0,0,0,0,0,0,120,253,174,63,0,0,0,0,0,0,26,207,253,141,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197,252,252,241,72,0,0,0,0,29,200,252,252,94,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197,252,252,253,186,13,0,0,120,215,252,245,118,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,175,227,252,253,252,77,44,169,253,252,252,208,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,112,253,252,252,252,252,253,252,141,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,255,253,255,253,254,253,170,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,224,253,251,253,251,253,251,225,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,197,254,253,254,253,254,253,254,253,254,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197,251,253,251,253,251,253,251,253,251,196,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,253,254,253,254,196,198,253,254,253,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,139,251,196,83,84,28,197,251,196,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,254,253,169,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,253,251,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,253,254,253,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,196,253,251,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,85,85,85,0,0,0,0,85,253,254,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,197,251,253,251,169,168,169,168,197,251,253,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,253,254,253,254,253,254,253,254,253,254,253,254,139,0,0,0,0,0,0,0,0,0,0,0,0,0,114,253,251,253,251,253,251,253,251,253,251,253,251,253,251,114,0,0,0,0,0,0,0,0,0,0,0,0,169,254,253,254,196,114,0,29,197,254,253,254,196,254,253,254,84,0,0,0,0,0,0,0,0,0,0,0,168,253,251,253,83,0,0,197,251,253,251,84,28,139,251,253,196,114,0,0,0,0,0,0,0,0,0,86,253,254,253,114,57,141,253,254,253,114,0,0,0,85,253,254,253,57,0,0,0,0,0,0,0,0,0,28,196,253,251,169,224,253,251,196,83,0,0,0,0,28,196,253,251,168,0,0,0,0,0,0,0,0,0,0,0,254,253,254,253,254,196,114,0,0,0,0,0,0,114,254,253,169,0,0,0,0,0,0,0,0,0,0,0,139,251,253,251,196,28,0,0,0,0,0,0,0,0,139,138,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,194,253,253,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,251,251,251,96,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,251,251,183,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,241,251,214,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,76,253,251,251,212,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,194,253,251,251,92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,199,253,251,251,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,136,251,253,251,196,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,136,251,253,187,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,61,225,251,253,134,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,119,253,253,255,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,251,251,251,225,72,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,251,251,251,194,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,251,251,251,164,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,61,228,251,251,175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,251,251,251,57,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,83,237,251,251,251,57,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,98,251,251,251,208,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,98,251,251,208,53,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,98,251,236,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,128,0,0,0,0,0,0,64,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,0,0,0,0,0,0,191,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,128,0,0,0,0,0,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,255,0,0,0,0,0,191,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,64,0,0,0,0,0,64,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,255,255,255,255,191,0,191,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,255,191,128,0,0,128,255,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,191,128,0,0,0,0,0,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,128,0,0,0,0,0,0,64,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,147,255,170,137,177,54,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,148,252,253,253,253,253,253,237,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,151,241,244,253,253,253,253,253,242,142,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,253,253,253,253,253,253,253,223,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,253,253,228,155,128,25,46,186,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,253,143,24,0,0,0,0,159,131,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,122,248,253,51,0,0,0,0,0,109,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,232,253,253,114,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,84,248,253,253,253,253,240,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,105,253,253,198,188,206,253,210,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,81,144,65,10,0,56,240,253,71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,253,71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,106,253,161,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,253,201,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,253,184,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,150,156,29,0,0,0,0,146,253,71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,118,253,253,213,29,0,0,53,230,253,71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,234,252,253,214,77,143,231,253,250,67,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,108,166,252,253,253,253,214,90,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,108,234,253,149,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,109,171,253,72,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,78,252,253,252,71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,37,161,232,252,237,215,61,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,105,206,253,252,252,168,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,109,110,149,252,252,253,158,41,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,161,252,253,252,252,252,222,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,181,252,253,252,252,168,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,221,252,253,252,252,252,207,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,233,253,253,255,253,253,253,255,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,252,252,252,222,179,179,221,253,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,94,215,215,132,41,0,6,160,253,179,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,252,253,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,99,253,255,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,242,252,222,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,94,252,252,125,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,144,144,0,0,0,0,145,237,252,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,218,253,253,83,0,16,109,255,253,237,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,154,231,252,154,115,222,252,253,231,71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,160,252,252,252,252,252,154,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,252,252,252,148,108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,33,26,0,0,0,0,0,0,0,0,62,91,213,254,255,254,255,158,6,0,0,0,0,0,0,0,0,0,85,68,0,0,0,0,0,0,0,6,177,253,253,253,253,253,253,253,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,83,253,253,253,253,253,253,253,253,210,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,189,253,253,253,238,220,220,142,253,253,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,214,253,161,71,0,0,222,253,253,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,40,10,0,0,0,222,253,253,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,222,253,253,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,250,253,186,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,206,253,253,78,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,95,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,79,238,253,251,148,5,0,0,0,0,0,0,0,0,0,0,0,0,0,49,99,99,9,0,0,0,0,74,169,253,253,131,0,0,0,0,0,0,0,0,0,0,0,0,0,80,189,220,253,253,195,189,135,25,89,237,253,253,240,29,0,0,0,0,0,0,0,0,0,0,0,0,8,173,253,253,253,253,253,253,253,253,253,253,253,159,67,0,0,0,0,0,0,0,0,0,0,0,0,0,149,253,253,253,253,253,253,253,253,253,253,253,159,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,180,253,253,253,253,253,253,253,253,253,253,253,138,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,253,253,253,253,253,253,253,234,235,253,253,239,42,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,253,253,253,253,253,214,106,72,72,162,253,253,167,148,19,0,0,0,0,0,0,0,0,0,0,0,0,1,116,253,217,179,89,11,0,0,0,99,253,253,241,94,2,0,0,0,0,0,0,0,0,0,0,0,0,0,38,89,46,0,0,0,0,0,0,35,211,253,106,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,110,253,253,253,255,253,253,253,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,252,252,252,253,252,252,252,238,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,195,215,215,154,82,211,252,237,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,155,252,252,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,171,252,252,189,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,145,176,217,253,252,179,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,99,242,252,252,253,252,92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,252,252,252,253,252,236,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,125,0,110,253,253,108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,252,220,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,252,215,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,252,215,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,253,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,253,252,71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,221,253,200,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,160,252,253,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,110,233,253,253,145,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,154,73,94,237,253,252,241,179,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,206,252,252,252,252,253,220,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,62,252,252,252,168,108,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,163,253,195,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,154,203,252,253,252,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,73,234,252,252,253,252,149,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,225,252,252,252,253,252,227,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,226,253,240,140,114,253,228,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,58,24,0,238,252,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,252,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,252,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,252,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,255,253,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,85,48,0,0,0,15,253,252,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,88,203,252,229,150,57,57,135,253,233,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,181,252,252,252,253,252,252,252,252,253,177,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,113,252,252,252,252,253,252,252,252,252,253,252,165,126,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,253,253,253,190,141,178,253,253,253,255,253,253,253,174,114,75,0,0,0,0,0,0,0,0,0,0,0,90,243,252,252,237,147,234,252,195,89,27,152,167,224,252,253,195,85,76,0,0,0,0,0,0,0,0,0,0,116,252,252,252,253,252,208,65,0,0,0,0,37,55,196,246,252,246,197,0,0,0,0,0,0,0,0,0,28,84,177,223,162,84,19,0,0,0,0,0,0,0,0,122,230,252,252,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,25,112,112,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,255,212,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,177,253,253,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,177,253,213,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,76,251,253,183,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,78,253,253,183,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,78,253,253,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,223,253,253,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,231,253,253,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,231,253,253,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,231,253,215,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,231,253,129,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65,242,242,67,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,131,253,230,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,138,253,230,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,253,253,240,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,253,253,253,205,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,187,253,253,233,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,253,253,220,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,179,253,253,143,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,253,173,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,121,255,253,253,211,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,107,191,247,252,253,252,252,252,213,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,25,197,252,252,252,252,159,103,229,252,192,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,67,252,239,158,39,39,0,34,235,226,57,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,53,44,0,0,0,0,212,252,136,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,118,249,232,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,118,215,253,252,223,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,218,252,252,253,252,252,223,125,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,173,252,251,238,176,106,154,250,252,66,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,82,119,113,0,0,0,0,142,252,66,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,81,253,129,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,81,252,198,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,81,252,198,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,193,252,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,172,200,109,25,0,0,0,0,75,244,252,66,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,81,252,252,252,93,0,0,0,0,192,252,202,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,81,252,252,252,183,0,0,20,97,244,197,58,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,219,252,252,242,83,27,134,252,252,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,241,252,252,252,252,253,246,119,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,140,252,252,252,190,69,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,121,176,253,140,121,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,107,184,240,253,252,252,252,252,243,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,119,252,252,252,253,252,252,252,252,252,198,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,187,252,252,252,252,253,218,172,197,252,252,212,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,186,252,241,185,108,53,53,31,0,116,252,252,176,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,252,241,97,0,0,0,0,0,20,221,252,241,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,213,252,177,0,0,0,0,0,39,189,252,241,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,238,252,66,0,0,2,27,91,247,252,243,165,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,94,252,252,76,106,146,152,252,253,252,252,95,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,94,252,252,252,252,252,252,252,253,252,202,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,214,253,253,253,240,127,0,255,253,159,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,13,13,13,11,0,128,253,207,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,240,253,145,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,252,253,104,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,146,252,253,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,146,252,228,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,219,252,113,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,218,252,252,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,252,251,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,252,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,79,109,255,253,133,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,181,252,253,252,247,93,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,73,232,252,253,252,252,211,182,181,181,181,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,217,252,252,253,252,252,252,253,252,252,252,144,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,170,252,252,252,206,143,143,143,253,252,252,189,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,252,252,241,179,20,0,11,155,253,252,179,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,215,91,61,0,0,73,191,252,237,91,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,145,237,252,252,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,253,237,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,155,253,189,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,58,252,253,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,181,252,191,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,73,253,253,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197,252,252,104,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,79,242,246,132,37,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,191,252,153,144,253,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,145,253,253,253,253,145,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,237,252,252,252,252,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,252,252,252,226,132,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,252,252,210,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,80,116,254,254,255,61,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,105,184,253,253,253,253,253,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,237,247,253,253,253,253,253,253,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,253,253,253,253,253,253,253,253,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,95,209,209,106,71,232,253,253,181,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,190,253,197,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,228,253,249,125,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,238,253,218,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,153,253,253,90,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,131,253,253,207,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,246,253,218,42,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,25,71,248,253,151,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,149,149,149,149,185,253,253,253,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,202,253,253,253,253,253,253,253,253,73,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,33,131,237,253,253,253,253,253,253,253,253,253,190,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,159,253,253,253,253,253,253,253,219,238,253,253,218,131,9,0,0,0,0,0,0,0,0,0,0,0,0,28,191,253,253,253,253,249,138,138,4,83,242,253,253,201,33,0,0,0,0,0,0,0,0,0,0,0,0,195,253,253,253,253,245,106,0,0,0,0,216,253,253,226,46,0,0,0,0,0,0,0,0,0,0,0,0,237,253,253,253,243,92,0,0,0,0,0,53,253,253,253,60,0,0,0,0,0,0,0,0,0,0,0,0,116,253,253,253,105,0,0,0,0,0,0,17,168,253,253,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,255,180,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,109,253,245,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,188,253,186,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,119,253,238,42,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,93,253,253,194,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,56,253,253,230,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,208,253,253,201,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,193,253,204,113,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,183,253,253,65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,142,253,221,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,219,253,253,157,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,156,253,253,214,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,228,253,247,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,253,253,170,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,91,214,253,199,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,73,242,253,253,81,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,33,213,253,253,199,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,139,253,253,253,81,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,246,253,207,20,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,179,253,98,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,115,189,254,255,232,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,119,237,253,253,253,253,188,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,235,253,253,253,253,253,232,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,253,253,253,253,253,253,253,53,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,253,253,253,253,253,253,223,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,229,253,253,253,253,253,253,53,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,162,247,250,253,253,253,219,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,195,253,227,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,249,253,131,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,140,253,246,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,78,238,253,132,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,95,169,213,225,253,253,243,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,189,231,231,253,253,253,253,253,253,241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,219,252,253,253,241,167,153,253,253,253,253,104,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,123,253,253,253,164,0,5,137,253,253,253,253,106,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,171,253,253,183,4,5,136,253,253,169,216,253,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,171,253,253,117,35,190,253,253,203,30,113,253,224,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,78,253,253,207,227,253,253,167,12,0,206,253,224,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,96,253,253,253,197,79,11,0,0,231,249,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,17,122,22,8,0,0,0,0,231,241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,191,255,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,191,255,255,255,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,191,255,255,255,255,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,191,255,255,255,255,255,255,191,191,128,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,255,255,191,255,255,255,255,255,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,191,128,64,128,128,128,191,255,255,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,128,0,0,0,0,0,0,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,128,0,0,0,0,64,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,128,0,0,128,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,191,0,255,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,255,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,191,255,255,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,128,0,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,255,128,0,64,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,128,0,128,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,255,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,191,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,191,255,255,255,255,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,255,255,255,191,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,128,255,255,255,255,191,0,0,128,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,191,255,255,255,255,255,191,128,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,191,128,128,191,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,64,0,64,64,0,0,0,191,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,64,0,0,64,0,0,64,191,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,128,0,0,0,0,0,128,191,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,0,0,0,0,0,191,255,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,191,128,128,191,255,255,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,255,128,191,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,191,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,167,252,61,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,200,248,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,59,222,254,253,147,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,254,254,254,254,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,254,254,254,254,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,254,254,254,254,26,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,254,254,254,254,254,147,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,204,254,254,251,77,182,254,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,207,254,254,251,71,11,27,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,25,220,254,120,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,254,245,72,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,231,255,206,19,28,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,189,254,254,206,72,110,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65,252,254,254,138,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,181,254,254,189,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,254,254,254,140,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,233,254,254,150,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,254,236,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,224,254,244,58,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,254,254,124,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,97,154,160,215,255,255,98,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,118,222,253,253,253,253,253,164,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,138,245,253,253,253,253,253,253,253,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,105,231,253,253,253,253,253,253,253,253,228,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,216,253,253,253,253,253,253,253,253,253,253,253,234,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,161,253,223,122,32,135,232,162,134,210,253,197,159,57,0,0,0,0,0,0,0,0,0,0,0,0,0,0,228,253,58,27,123,145,30,18,202,253,242,65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,161,253,215,155,191,108,19,115,253,241,152,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,253,253,191,11,19,115,253,253,110,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,162,253,235,196,210,253,253,184,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,186,244,253,253,253,253,184,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,156,253,253,253,241,57,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,92,204,253,253,253,253,194,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,91,245,253,253,241,225,253,234,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,235,253,253,242,152,26,216,253,161,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,169,253,253,155,0,0,82,220,253,144,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,253,253,253,38,0,0,27,215,253,78,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,54,244,253,253,160,143,40,14,126,253,144,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,237,253,253,253,253,253,253,253,253,78,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,111,123,234,253,253,253,253,253,78,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,191,128,64,128,191,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,0,0,0,128,255,191,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,255,191,0,0,128,255,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,255,128,0,0,191,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,64,0,64,191,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,128,64,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,255,191,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,191,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,191,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,128,255,255,191,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,191,0,191,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,255,128,0,191,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,128,0,0,191,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,191,128,64,128,191,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,255,255,255,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,191,128,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,191,255,191,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,255,191,64,128,128,128,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,128,128,191,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,191,0,128,0,0,191,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,64,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,96,96,96,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,92,190,221,251,251,253,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,127,253,251,251,251,251,253,141,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,104,251,253,251,251,251,251,253,248,111,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,158,240,251,253,231,94,94,193,253,251,204,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,214,253,253,253,159,0,0,0,60,255,253,253,31,0,0,0,0,0,0,0,0,0,0,0,0,0,20,253,251,251,251,251,158,4,112,190,228,253,251,251,197,91,0,0,0,0,0,0,0,0,0,0,0,32,205,253,251,251,251,251,206,142,188,126,204,253,251,251,251,251,0,0,0,0,0,0,0,0,0,0,0,64,251,253,251,219,148,31,91,168,15,0,19,31,157,251,251,211,0,0,0,0,0,0,0,0,0,0,0,162,251,253,231,47,0,0,0,0,0,0,0,0,32,251,251,94,0,0,0,0,0,0,0,0,0,12,174,253,253,219,39,0,0,0,0,0,0,0,0,0,32,253,229,59,0,0,0,0,0,0,0,0,0,151,251,251,231,39,0,0,0,0,0,0,0,0,0,0,151,251,168,0,0,0,0,0,0,0,0,0,0,190,251,251,94,0,0,0,0,0,0,0,0,0,0,0,190,251,31,0,0,0,0,0,0,0,0,0,84,244,251,156,12,0,0,0,0,0,0,0,0,16,64,223,244,219,23,0,0,0,0,0,0,0,0,0,96,251,251,31,0,0,0,0,0,0,0,0,80,181,251,253,132,47,0,0,0,0,0,0,0,0,0,0,255,253,253,31,0,96,84,0,0,0,96,214,253,253,253,100,0,0,0,0,0,0,0,0,0,0,0,0,253,251,251,197,190,253,244,190,190,190,253,247,220,180,62,0,0,0,0,0,0,0,0,0,0,0,0,0,173,251,251,251,251,253,251,251,251,251,126,110,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,31,110,188,188,189,69,31,31,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,178,255,107,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,253,229,42,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,78,240,253,99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,78,239,253,169,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,76,239,253,220,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,243,253,135,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,192,253,138,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,136,253,234,41,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,181,253,249,192,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,225,253,230,0,0,0,149,177,172,53,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,225,253,138,0,55,114,244,253,253,247,104,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,75,249,253,175,213,241,253,253,253,253,253,246,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197,253,253,253,253,253,253,253,253,253,253,253,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,209,253,253,253,251,158,32,5,5,66,194,253,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,253,253,239,157,89,0,0,0,0,0,30,248,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,253,253,108,0,0,0,0,0,0,30,170,167,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,253,253,42,0,0,0,0,0,60,217,193,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,253,253,170,84,84,84,174,201,249,150,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,112,253,253,253,253,253,253,253,161,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,122,193,253,234,135,135,36,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,188,255,164,0,0,0,0,0,112,118,118,99,0,0,0,0,0,0,0,0,0,0,0,0,0,21,116,245,253,253,246,234,234,234,234,234,252,253,253,250,88,0,0,0,0,0,0,0,0,0,0,0,0,171,253,253,253,253,253,253,253,253,253,253,253,253,253,218,15,0,0,0,0,0,0,0,0,0,0,0,47,206,253,250,198,64,61,61,61,61,61,61,84,235,240,92,0,0,0,0,0,0,0,0,0,0,0,14,219,253,253,146,0,0,0,0,0,0,0,0,123,253,191,0,0,0,0,0,0,0,0,0,0,0,0,104,253,253,233,42,0,0,0,0,0,0,0,75,248,253,184,0,0,0,0,0,0,0,0,0,0,0,0,234,253,253,147,0,0,0,0,0,0,0,0,125,253,253,54,0,0,0,0,0,0,0,0,0,0,0,0,58,143,114,2,0,0,0,0,0,0,0,7,250,253,172,2,0,0,0,0,0,0,0,0,0,0,0,0,0,164,33,0,0,0,0,0,0,0,25,228,253,253,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,0,0,0,0,0,0,0,14,163,253,253,225,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,75,253,253,253,125,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,97,253,253,172,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,253,253,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,34,219,253,246,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,126,253,227,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,123,247,253,181,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,121,248,253,253,122,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,249,253,253,245,189,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130,253,253,236,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130,253,204,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,134,253,138,105,62,0,7,105,105,105,183,253,208,0,0,0,0,0,0,0,0,0,0,0,0,0,18,78,231,252,252,252,235,208,211,252,252,252,252,252,171,0,0,0,0,0,0,0,0,0,0,0,0,0,84,252,252,252,252,252,253,252,252,252,238,247,252,252,59,0,0,0,0,0,0,0,0,0,0,0,0,57,237,252,252,252,160,132,133,132,132,132,15,178,252,209,31,0,0,0,0,0,0,0,0,0,0,18,84,237,252,243,177,125,7,0,0,0,0,36,119,243,212,27,0,0,0,0,0,0,0,0,0,0,0,78,252,252,252,239,70,0,0,0,0,0,0,49,235,252,197,8,0,0,0,0,0,0,0,0,0,0,0,208,252,252,212,104,0,0,0,0,0,0,8,189,252,156,56,0,0,0,0,0,0,0,0,0,0,0,0,208,246,92,10,0,0,0,0,0,0,22,100,252,252,74,0,0,0,0,0,0,0,0,0,0,0,0,0,49,55,0,0,0,0,0,0,0,0,87,252,252,168,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,208,252,181,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,62,255,253,253,133,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,173,253,252,252,203,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,185,253,252,242,125,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,197,253,252,88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,89,252,253,252,176,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,82,235,252,253,209,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,245,252,252,253,113,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,134,252,252,252,216,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,224,252,244,207,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,252,252,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,255,191,255,128,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,191,255,255,64,0,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,255,0,0,64,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,191,0,0,0,128,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,0,0,0,128,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,0,0,128,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,128,191,255,191,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,255,191,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,255,255,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,255,255,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,191,64,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,191,128,191,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,0,191,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,191,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,255,190,82,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,164,180,251,253,253,246,180,154,17,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,230,253,253,253,253,253,253,253,253,195,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,197,248,253,253,253,253,253,253,253,253,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,61,148,253,253,253,253,169,129,222,253,253,253,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,148,253,253,253,253,151,13,0,51,253,253,227,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,124,253,253,253,127,114,3,0,55,150,253,230,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,219,240,187,18,0,0,0,99,253,253,238,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,79,0,0,0,0,6,194,253,253,111,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,88,253,245,97,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,83,253,253,244,90,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,221,253,228,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,174,189,189,232,243,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,191,253,253,253,253,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,48,234,253,253,246,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,73,242,253,253,159,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,139,253,253,253,120,58,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,206,253,253,253,253,195,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,98,253,253,253,181,179,110,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,108,253,253,253,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,101,101,199,255,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,47,47,183,236,253,253,253,253,199,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,77,179,253,253,253,253,253,253,159,107,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,250,253,253,253,253,238,7,7,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,253,253,226,139,61,57,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,253,253,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,81,241,253,180,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,198,253,253,185,31,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,73,246,253,253,253,253,193,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,83,243,253,253,253,253,253,253,191,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,193,253,253,250,230,136,246,253,253,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,193,253,211,114,0,0,177,253,253,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,30,20,0,0,0,27,174,253,247,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,253,253,238,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,177,253,253,238,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,62,203,242,223,191,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,69,0,6,8,153,173,253,253,248,126,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,223,169,109,218,253,253,253,253,220,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,253,253,253,253,253,235,180,45,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,128,253,253,135,99,66,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,73,73,73,73,73,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,110,181,253,252,252,252,253,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,252,253,252,252,252,253,179,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,94,211,252,253,252,252,252,253,252,71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,144,252,252,253,252,252,252,253,252,216,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,120,212,252,226,215,72,195,112,154,253,252,236,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,252,253,252,71,0,0,0,0,0,108,232,236,144,0,0,0,0,0,0,0,0,0,0,0,0,0,156,253,253,192,15,0,0,0,0,0,0,63,238,232,47,0,0,0,0,0,0,0,0,0,0,0,84,217,227,252,128,35,5,0,0,0,0,0,0,63,237,252,148,0,0,0,0,0,0,0,0,0,0,27,181,252,241,195,31,0,0,0,0,0,0,0,0,79,242,231,87,0,0,0,0,0,0,0,0,0,42,160,252,252,97,0,0,0,0,0,0,0,0,0,0,191,252,231,46,0,0,0,0,0,0,0,0,0,32,212,253,253,35,0,0,0,0,0,0,0,32,31,63,255,253,154,0,0,0,0,0,0,0,0,0,32,207,252,210,118,5,0,0,0,0,11,73,218,227,206,175,253,231,51,0,0,0,0,0,0,0,0,0,47,232,252,190,0,0,0,0,0,0,37,190,253,252,252,252,253,158,0,0,0,0,0,0,0,0,0,0,109,252,252,190,0,42,144,144,145,144,221,252,253,252,252,168,108,15,0,0,0,0,0,0,0,0,0,0,145,238,253,253,253,253,253,253,255,253,253,253,145,104,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,179,179,210,210,252,210,180,138,35,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,31,71,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,78,254,254,237,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,25,96,238,253,253,253,119,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,114,253,253,253,253,253,119,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,198,255,253,253,253,182,253,119,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,107,253,254,235,81,29,12,56,119,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,253,253,180,57,0,0,0,31,134,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,247,253,253,71,0,0,0,0,31,253,94,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,82,247,253,237,75,0,0,0,0,0,20,211,163,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,179,253,253,208,0,0,0,0,0,0,0,32,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,133,223,253,253,208,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,121,254,254,254,217,124,0,0,87,202,254,122,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,253,253,221,37,0,0,123,246,253,253,253,197,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,128,253,251,112,6,42,164,254,253,253,253,253,223,0,0,0,0,0,0,0,0,0,0,0,0,0,0,87,253,253,152,0,161,253,253,254,253,253,253,253,223,0,0,0,0,0,0,0,0,0,0,0,0,0,0,164,253,253,74,0,239,253,253,223,178,90,214,253,223,0,0,0,0,0,0,0,0,0,0,0,0,0,0,165,253,253,74,0,240,253,146,44,0,2,135,253,223,0,0,0,0,0,0,0,0,0,0,0,0,0,0,164,253,253,74,0,239,253,183,0,32,142,253,253,223,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,253,253,243,239,252,253,253,241,243,253,253,226,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,129,248,253,253,253,253,253,254,253,224,208,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,92,210,253,253,253,253,192,104,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,191,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,128,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,96,96,214,253,114,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,32,191,190,190,205,251,253,251,251,251,212,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,143,251,253,251,251,251,251,253,251,251,251,251,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,229,251,253,251,251,251,251,253,251,251,251,211,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,205,251,251,253,251,172,94,193,253,251,251,211,35,0,0,0,0,0,0,0,0,0,0,0,0,0,96,214,253,253,253,159,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,253,251,251,251,251,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,158,253,251,251,251,251,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,158,253,251,251,251,251,242,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,59,193,94,94,232,251,253,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,223,253,255,221,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,221,251,253,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,0,0,0,0,0,0,221,251,253,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,223,240,111,0,0,0,8,143,248,251,169,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,251,126,0,0,0,32,251,251,251,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,253,253,213,36,96,214,253,253,253,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,233,251,251,251,212,253,251,251,243,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,220,251,251,251,253,251,251,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,220,251,251,253,243,109,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,232,251,95,82,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,255,253,169,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,224,253,251,225,168,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,253,254,253,254,253,254,139,85,85,141,139,57,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197,251,253,251,253,251,253,251,253,251,253,251,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,254,253,254,253,254,253,254,253,254,253,254,253,169,0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,253,251,253,251,253,251,253,251,253,251,253,251,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,253,254,253,169,168,114,0,85,253,254,253,169,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,139,251,196,83,0,0,0,0,85,251,253,251,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,253,254,253,169,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197,251,253,251,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,254,253,254,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,224,253,251,196,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,253,254,253,169,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197,251,253,251,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,254,253,254,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,224,253,251,196,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,253,254,253,169,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,251,253,251,168,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,198,253,254,253,198,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,251,253,251,196,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,206,246,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,161,254,254,111,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,204,254,178,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,94,254,244,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,246,254,228,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,94,254,254,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,127,179,20,0,89,243,254,183,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,254,254,58,0,184,254,254,91,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,201,254,197,12,48,240,254,223,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,193,254,254,82,17,177,254,243,93,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,105,247,254,254,82,97,254,254,178,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,76,248,254,254,254,234,238,254,254,78,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,199,254,254,254,254,254,255,254,194,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,254,254,254,221,227,254,254,254,71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,212,222,107,14,163,254,254,254,71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,53,237,254,254,218,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,254,254,254,195,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,159,254,254,251,119,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,237,254,253,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45,235,254,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,190,255,253,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,140,252,253,252,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,185,252,253,212,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,139,252,252,215,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,184,252,252,99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,155,252,249,198,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,75,252,252,217,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,228,252,221,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,252,252,93,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,227,252,252,93,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,81,253,253,213,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,136,252,229,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,227,252,198,0,0,0,0,0,40,94,206,226,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,94,252,252,198,0,0,0,0,144,229,252,252,252,244,110,0,0,0,0,0,0,0,0,0,0,0,0,0,94,252,252,198,0,0,46,200,253,252,252,252,252,252,222,25,0,0,0,0,0,0,0,0,0,0,0,0,94,252,252,198,0,119,224,252,253,252,252,252,252,252,252,93,0,0,0,0,0,0,0,0,0,0,0,0,79,246,252,235,173,223,252,252,253,252,252,252,252,252,203,29,0,0,0,0,0,0,0,0,0,0,0,0,0,128,252,252,252,252,252,252,253,252,252,229,225,127,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,34,140,238,238,247,252,252,240,112,106,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,160,252,181,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,206,253,153,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,143,221,251,251,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,190,251,251,251,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,84,244,251,251,113,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,194,251,251,211,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,255,253,253,91,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,190,253,251,156,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,251,253,251,126,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,162,251,253,223,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,181,251,253,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,234,253,219,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,221,251,251,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,251,251,251,158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,171,251,251,152,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,190,251,251,152,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,253,253,189,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,253,251,235,66,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,158,253,251,188,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,217,253,223,31,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,158,253,220,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,115,253,253,253,195,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,190,214,251,251,251,251,253,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,143,251,253,251,251,251,251,253,220,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,190,248,251,253,251,251,251,251,253,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,205,251,251,253,251,172,133,251,253,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,253,253,219,39,0,0,159,255,221,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,189,180,62,39,0,0,147,228,253,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,143,251,253,220,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,221,251,253,180,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,158,240,251,95,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,174,253,253,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,32,52,111,72,190,131,197,251,251,231,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,205,253,251,251,251,251,253,251,251,251,94,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,104,251,253,251,251,251,251,253,251,251,251,232,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,221,251,253,251,251,251,251,253,251,251,251,251,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,242,253,255,253,253,253,253,255,181,206,253,253,195,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,103,251,253,251,251,251,231,161,16,32,229,251,253,220,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,251,253,251,188,126,47,0,0,0,221,251,253,220,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,129,31,31,15,0,0,0,0,0,181,251,253,81,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,193,253,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,92,253,192,12,0,0,0,0,0,0,0,0,114,113,113,101,0,0,0,0,0,0,0,0,0,0,0,51,243,252,253,181,19,0,0,0,76,117,225,225,253,252,252,249,100,0,0,0,0,0,0,0,0,0,0,88,252,252,253,252,100,73,197,198,246,252,252,252,253,252,252,252,112,0,0,0,0,0,0,0,0,0,57,234,252,252,253,252,252,252,252,253,252,230,208,161,253,252,252,138,37,0,0,0,0,0,0,0,0,0,163,252,252,252,253,252,252,252,252,112,112,25,0,79,253,252,220,37,0,0,0,0,0,0,0,0,0,51,238,253,253,253,255,215,110,0,0,0,0,0,13,191,255,253,133,0,0,0,0,0,0,0,0,0,0,238,252,252,242,89,27,18,0,0,0,0,0,51,228,252,253,51,6,0,0,0,0,0,0,0,0,0,0,143,252,208,96,0,0,0,0,0,0,0,0,88,252,252,228,21,0,0,0,0,0,0,0,0,0,0,0,38,84,19,0,0,0,0,0,0,0,13,154,234,249,223,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,252,252,145,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,141,255,253,228,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,73,240,253,252,148,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45,234,252,253,76,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,73,252,252,240,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197,252,252,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,226,253,237,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,225,252,242,74,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,120,246,252,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,126,253,252,214,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,174,252,195,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,130,225,255,255,212,130,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,94,219,253,253,253,253,253,253,207,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,101,219,253,249,235,235,235,250,253,253,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,120,253,253,208,79,80,18,0,190,238,244,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,108,253,253,208,36,123,230,188,6,0,37,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,169,253,226,79,182,253,253,242,42,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,25,253,253,239,226,253,253,253,179,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,25,253,253,253,253,253,253,253,179,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,232,253,244,197,149,253,253,179,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,68,56,0,87,253,253,148,0,0,0,0,0,0,0