# This file gives reference implementations of BQN primitives assuming # limited initial functionality. Implementations are designed to be # simple and not fast. # In some cases an operation is defined with limited functionality at # first and later expanded. For convenience, rather than renaming these # limited versions, every primitive use refers to the most recent # definition in source code, as if redefinitions shadowed previous # primitive definitions. #⌜ # LAYER 0: Assumed functionality # Arithmetic on the implementation-defined number system # LIMITED to the stated cases and atomic arguments. + # Add - # Negate Subtract × # Multiply ÷ # Reciprocal Divide ⋆ # Exponential Power ⌊ # Floor = # Equals ≤ # Less Than or Equal to # Other basic functionality that we need to assume Type # 0 if 𝕩 is an array, 1 if a number, >1 otherwise ! # 𝕩 is 0 or 1; throw an error if it's 0 ≢ # LIMITED to monadic case ⥊ # LIMITED to array 𝕩 and (×´𝕨)≡≢𝕩 ⊑ # LIMITED to natural number 𝕨 and list 𝕩 _amend # {𝕨˙⌾(𝕗⊸⊑)𝕩} for list 𝕩 ↕ # LIMITED to number 𝕩 Identity # Left or right identity of function 𝕏 ⁼ # Inverse of function 𝔽 Fill # Enclosed fill value for 𝕩 HasFill # Whether 𝕩 has a fill value #⌜ # LAYER 1: Foundational operators and functions # Combinators ◶ ← {𝕨((𝕨𝔽𝕩)⊑𝕘){𝔽}𝕩} # LIMITED to number left operand result ˙ ← {𝕩⋄𝕗} ⊘ ← {𝔽𝕩;𝕨𝔾𝕩} ⊢ ← {𝕩} ⊣ ← {𝕩;𝕨} ˜ ← {𝕩𝔽𝕨⊣𝕩} ∘ ← {𝔽𝕨𝔾𝕩} ○ ← {(𝔾𝕨)𝔽𝔾𝕩} ⊸ ← {(𝔽𝕨⊣𝕩)𝔾𝕩} ⟜ ← {(𝕨⊣𝕩)𝔽𝔾𝕩} ⍟ ← {𝔾◶⊢‿𝔽} # LIMITED to boolean right operand result IsArray ← 0=Type Int ← (1=Type)◶⟨0,⌊⊸=⟩ # Is an integer (including ¯∞ and ∞) Nat ← (1=Type)◶⟨0,0⊸≤×⌊⊸=⟩ # Is a natural number ≢ ↩ IsArray◶⟨⟩‿≢ # LIMITED to monadic case ⋈ ← {⟨𝕩⟩;⟨𝕨,𝕩⟩} # LIMITED to numeric arguments for arithmetic cases √ ← ⋆⟜(÷2) ⊘ (⋆⟜÷˜) # Higher precision allowed; see spec ∧ ← × ∨ ← +-× ¬ ← 1+- | ← ×⟜× ⊘ {𝕩-𝕨×⌊𝕩÷𝕨} # Higher precision allowed; see spec < ← {⟨⟩⥊⟨𝕩⟩} ⊘ (¬≤˜) > ← (¬≤) ≥ ← !∘0 ⊘ (≤˜) ≠ ← Length ⊘ (¬=) = ↩ Rank ⊘ = × ↩ 0⊸(<->) ⊘ × # ⌊⌈ defined after pervasion below ¨ ← _eachm # LIMITED to monadic case and array 𝕩 ´ ← _fold Rank ← 0⊑≢∘≢ Length ← (0⟜0) 𝕩} r } # Re-add identities for needed redefined functions identity { f‿i←𝕨 ⋄ f˙⊸=◶𝕩‿i }´↩ ⟨ ×‿1, ∨‿0, ∧‿1 ⟩ #⌜ # LAYER 2: Pervasion # _pervasive should be applied to all arithmetic as an IMPLICIT STEP: # +-×÷⋆√⌊⌈|¬ and dyadic ∧∨<>≠=≤≥ # Join: elements 0…≠𝕨 come from 𝕨 and the rest from 𝕩 ∾ ← {k←≠𝕨⋄k⊸≤◶⟨⊑⟜𝕨,-⟜k⊑𝕩˜⟩¨↕k+≠𝕩} # LIMITED to two list arguments ToArray ← IsArray◶<‿⊢ _table←{ m←≠a←⥊𝕨 ⋄ n←≠b←⥊𝕩 ⋄ F←𝔽 ⋄ i←0 # 𝕩 is the result index; maintain index i in 𝕨 and compute 𝕩-n×i in 𝕩 (𝕨∾○≢𝕩)⥊{i+↩𝕩=n×i+1⋄(i⊑a)F(𝕩-n×i)⊑b}¨↕m×n } _eachd←{ _e←{ # 𝕨 has rank less than or equal to 𝕩 k←≠p←≢𝕨 ⋄ q←≢𝕩 ! ∧´(⊑⟜p=⊑⟜q)¨↕k # p≡k↑q l←×´(q⊑˜k⊸+)¨↕q≠⊸-k # ×´k↓q, size of a cell in 𝕩 (pairs with a unit) a←⥊𝕨 ⋄ b←⥊𝕩 # In the inner function, 𝕨 indexes into a and (l×𝕨)+𝕩 into b q⥊ (≠a) (⊑⟜a 𝔽 l⊸×⊸+⊑b˙)_table○↕ l } >○=◶⟨𝔽_e,𝔽˜_e˜⟩ } ⌜ ← {(𝔽_eachm)⊘(𝔽_table)○ToArray} ¨ ↩ {(𝔽_eachm)⊘(𝔽_eachd)○ToArray} # Pervasion recurses whenever any argument is an array _pervasive←{ ⊢⊘∨○IsArray◶⟨𝔽, 𝔽{𝕨𝔽_pervasive𝕩}¨⟩ } # The modifier _pervasive applies to all arithmetic. # For practicality when testing, basic functions are made pervasive # instead, which works for all arithmetic except dyadic ⌊⌈. ⌊ ↩ ⌊ ⊘ ({(𝕨>𝕩)⊑𝕨‿𝕩}_pervasive) ⌈ ← -∘⌊∘- ⊘ ({(𝕨<𝕩)⊑𝕨‿𝕩}_pervasive) #⌜ # LAYER 3: Remove other limits # After this all implementations are full except ∾; ↕ is monadic only Deshape ← IsArray◶{⟨𝕩⟩}‿⥊ Reshape←{ ! 1≥=𝕨 s←Deshape 𝕨 sp←+´p←¬Nat⌜s # Number of non-numeric elements ! 1≥sp # At most one allowed n←≠d←Deshape 𝕩 l←sp◶(×´)‿{ # Handle computed shapes lp←×´p 1⍟⊣¨𝕩 # Product of numeric elements ! 0 ↩ Merge⍟IsArray ⊘ > ≍ ← >∘⋈ ⎉ ← _rankOp_ ⚇ ← _depthOp_ ⍟ ↩ _repeat_ ˘ ← {𝔽⎉¯1} ˝ ← _insert ` ← _scan Drop1 ← {⊑⟜𝕩¨𝕨+↕𝕨-˜≠𝕩} # Drop from list Cell ← Drop1⟜≢ # (-𝕨)-cell shape of array 𝕩 # Merge: if empty, append fill shape to array shape to reshape fill # Otherwise, run all element ravels together Merge←(0<≠∘⥊)◶((∾○≢⥊⊢)⟜Fill⍟HasFill)‿{ c←≢⊑𝕩 ! ∧´⥊(c≡≢)¨𝕩 # Shapes must match 𝕩⊑⟜ToArray˜⌜↕c # Shape is (≢𝕩)∾c, with corresponding axis structure } # Check if ranks/depths are valid: list of one to three integers # Return ⥊𝕩 ValidateRanks←{ ! 1≥=𝕩 𝕩⥊↩ ! (1⊸≤∧≤⟜3)≠𝕩 ! ∧´Int¨𝕩 𝕩 } # Extract the appropriate ranks/depths for a call # Use negative indexing and wrap with | _ranks ← {⟨2⟩⊘⟨1,0⟩ ((⊣-1+|)˜⟜≠⊑¨<∘⊢) ValidateRanks∘𝔽} _depthOp_←{ n ← 𝕨 𝔾_ranks 𝕩 neg←0>n ⋄ F←𝔽 _d←{ R←(𝕗+neg)_d # Increment rec ← 2⥊(neg∧𝕗≥0)∨(0⌈𝕗)≥𝕨⋈○≡𝕩 # Whether to recurse into 𝕨⊣𝕩 and 𝕩 𝕨 rec◶(⟨R¨,R⟜(𝕩˙)¨∘⊣⟩≍⟨(𝕨{𝕏;𝕨˙⊸𝕏}r)¨∘⊢,F⟩) 𝕩 } 𝕨 n _d 𝕩 } _rankOp_←{ k←𝕨(⋈○= 0⊸≤◶⟨⌊⟜-,0⌈-⟩¨ 𝔾_ranks)𝕩 # Effective frame length # Enclose (-𝕨)-cells of 𝕩, that is, <⎉((=𝕩)-𝕨) 𝕩 EncK←{ f←⊑⟜(≢𝕩)¨↕𝕨 # 𝕨↑≢𝕩 c←×´s←𝕨Cell𝕩 f⥊⊑⟜(⥊𝕩)¨∘((s⥊↕c)+c×⊢)¨↕×´f } # Use <⊢ if k=0 (required to handle atoms correctly), and <⌜⊢ if k==𝕩 Enc←(>⟜0×1+≥⟜=)◶⟨<⊢,EncK,<⌜⊢⟩ > ((⊑k)Enc𝕨) 𝔽¨ ((1-˜≠)⊸⊑k)Enc𝕩 } _insert←{ ! 1≤=𝕩 𝕨 𝔽´ <˘𝕩 } _scan←{ ! IsArray 𝕩 ! 1≤=𝕩 c←×´cs←1 Cell 𝕩 ! (cs≡≢)𝕨 l←≠r←⥊𝕩 ⋄ F←𝔽 𝕨 (0|𝕩⋄l⌊↩𝕩⋄u⌈↩𝕩}⚇0 n b←𝕨{⊢;𝕨{𝕗˙⊸𝕏}}0 # Bind 𝕨 to 𝕏 if necessary i←⟨𝕩⟩⋄P←{𝕏⊣}∘B⊸{𝕎`i∾↕𝕩} # P makes a list of repetition 0, 1, … pos←𝕗 P u # Positive repetitions neg←𝕗 0⊸<◶⟨i,{𝕏⁼}⊸P⟩ -l # Negative repetitions (|⊑<⟜0⊑pos‿neg˙)⚇0 n } #⌜ # LAYER 5: Structural functions ⊏ ← 0⊸Select ⊘ Select ↑ ← Prefixes ⊘ Take ↓ ← Suffixes ⊘ Drop ↕ ↩ ↕ ⊘ Windows » ← Nudge ⊘ ShiftBefore « ← NudgeBack ⊘ ShiftAfter ⌽ ← Reverse ⊘ Rotate / ← Indices ⊘ Replicate # Multi-axis primitive: 𝔾 is applied to 𝕨 and determines the maximum # depth of the one-axis case _onAxes_←{ F←𝔽 (𝔾<≡)∘⊣◶{ # One axis ! 1≤=𝕩 𝕨F𝕩 }‿{ # Multiple axes ! 1≥=𝕨 ! (≠𝕨)≤=𝕩 R←{(⊑𝕨)F(1 Drop1 𝕨)⊸R˘𝕩}⍟{0<≠𝕨} # Recurse, then handle one axis 𝕨R𝕩 }⟜ToArray } SelSub←{ ! IsArray 𝕨 ! ∧´⥊Int¨ 𝕨 # All integers ! ∧´⥊ 𝕨 (≥⟜-∧<) ≠𝕩 # In range 𝕨 +↩ (≠𝕩)×𝕨<0 # Wrap negatives c←×´s←1 Cell 𝕩 ⊑⟜(⥊𝕩)¨(c×𝕨)+⌜s⥊↕c # Extend each index to a whole cell } Select←ToArray⊸(SelSub _onAxes_ 1) JoinTo←{ s←𝕨⋈○≢𝕩 a←1⌈´k←≠¨s # Argument ranks k and result rank a ! ∧´1≥a-k # Can add at most one axis c←(k¬a)+⟜(↕a-1)⊸⊏¨s # Cell shapes ! ≡´c l←+´(a=k)⊣◶1‿(⊑⊢)¨s # Total length (⟨l⟩∾⊑c)⥊𝕨∾○⥊𝕩 } Take←{ T←{ ! Int 𝕨 l←≠𝕩 # Indices, with clamp and modulus so that fills are all l i←(l+1)|¯1⌈l⌊((𝕨<0)×𝕨+l)+↕|𝕨 # Don't get the fill if not needed, as it can error i⊏JoinTo⟜(1⊸Cell⥊Fill)⍟(∨´l=i)𝕩 } # Add leading 1s to shape of 𝕩 first if necessary 𝕨 T _onAxes_ 0 (⟨1⟩⥊˜0⌈𝕨-○≠⊢)⊸∾∘≢⊸⥊𝕩 } Drop←{ s←(≠𝕨)(⊣↑⊢∾˜1⥊˜0⌈-⟜≠)≢𝕩 # Padded shape ((s׬⊸-𝕨>0)+(-s)⌈s⌊𝕨)↑𝕩 # Clamp and complement, then use Take } Prefixes ← {!1≤=𝕩 ⋄ (↕1+≠𝕩)Take¨<𝕩} Suffixes ← {!1≤=𝕩 ⋄ (↕1+≠𝕩)Drop¨<𝕩} ShiftBefore ← {!𝕨1⊸⌈⊸≤○=𝕩 ⋄ ( ≠𝕩) ↑ 𝕨 JoinTo 𝕩} ShiftAfter ← {!𝕨1⊸⌈⊸≤○=𝕩 ⋄ (-≠𝕩) ↑ 𝕩 JoinTo 𝕨} Nudge ← (1↑0↑⊢)⊸ShiftBefore NudgeBack ← (1↑0↑⊢)⊸ShiftAfter Windows←{ ! 1≥=𝕨 ! (≠𝕨)≤=𝕩 ! ∧´Nat¨⥊𝕨 s←𝕨≠⊸↑≢𝕩 ! ∧´𝕨≤1+s 𝕨{ i ← s(¬+⌜○↕⊢)⥊𝕨 # Cell indices in 𝕩: add leading and trailing axes c ← ><¨⊸⊏⟜𝕩¨i # Select the cells ((≢i)∾𝕨≠⊸↓≢𝕩)⥊c # Reshape in case it's empty }⍟(0<≠𝕨)𝕩 }⟜ToArray Reverse ← {!1≤=𝕩 ⋄ (-↕⊸¬≠𝕩)⊏𝕩} Rotate ← {!Int𝕨 ⋄ l←≠𝕩⋄(l|𝕨+↕l)⊏𝕩} _onAxes_ 0 Indices←{ ! 1==𝕩 ! ∧´Nat¨𝕩 ⟨⟩∾´𝕩⥊¨↕≠𝕩 } Rep ← Indices⊸⊏ # Expand unit 𝕨 to length of 𝕩, and check length match for a list Replicate ← ({0<=𝕨}◶(⊣˘)‿{!𝕨=○≠𝕩⋄𝕨}Rep⊢) _onAxes_ (1-0=≠) #⌜ # LAYER 6: Everything else ∾ ↩ Join ⊘ JoinTo ⊔ ← GroupInds ⊘ Group ⍉ ← Transpose ⊘ ReorderAxes ∊ ← MarkFirst ⊘ (IndexOf˜<≠∘⊢) ⍷ ← ∊⊸/ ⊘ Find ⊐ ← ⍷⊸IndexOf ⊘ IndexOf ⍋ ← Cmp _grade ⊘ ( Cmp _bins) ⍒ ← -∘Cmp _grade ⊘ (-∘Cmp _bins) ∧ ↩ ⍋⊸⊏ ⊘ ∧ ∨ ↩ ⍒⊸⊏ ⊘ ∨ ⊒ ← OccurrenceCount⊘ ProgressiveIndexOf identity { f‿i←𝕨 ⋄ f˙⊸=◶𝕩‿i }´↩ ⟨ ∨‿0, ∧‿1 ⟩ # Join of empty: multiply shape by (leading) fill shape JoinEmpty ← ({!𝕨≤○≠𝕩⋄𝕨≠⊸((𝕨×↑)∾↓)𝕩}○≢⥊⊢)⟜Fill⍟HasFill Join←(0<≠∘⥊)◶⟨JoinEmpty, (0<=)◶{!IsArray𝕩⋄>𝕩}‿{ ! IsArray 𝕩 s←≢¨𝕩 a←(≢𝕩){(s⊑˜(j=𝕩)⊸×)¨↕𝕨}¨j←↕r←=𝕩 # Skeleton: elements 0,0,…k,…0 along each axis h←(¬⟜(⌈´)≠¨)¨a # Which positions have the axis ! ∧´∧´¨0≤h # Rank can only change by 1 along a line o←+`⊑¨h # Starting position of each axis in ⊑𝕩 t←(¯1⊑o)↓⊑s # Trailing axes l←h{𝕎𝕩}¨˜(»o){⊣◶⟨1,𝕨⊸⊑⟩¨⟜𝕩}¨a # Length of each position on each axis ! s≡(i<¨⊸⊏⍟(0<≠∘⊣)¨l/𝕩 }⟩ Group←{ ! IsArray 𝕩 𝕨 ⋈∘ToArray⍟(2>≡)↩ ! 1==𝕨 {!∧´Int¨𝕩⋄!∧´¯1≤𝕩}∘⥊¨𝕨 # Atoms in 𝕨 must be integers ≥¯1 n←+´r←=¨𝕨 # Number of ranks grouped for each result axis (r); total (n) ! n≤=𝕩 ld←(∾≢⌜𝕨)-n↑≢𝕩 # Extra elements ! ∧´(0⊸≤∧≤⟜(r/1=r))ld # Must be 0 extra, or possibly 1 for rank 1 components dr←r⌊(0»+`r)⊏ld∾⟨0⟩ # Whether each result axis has a minimum length s←dr⊣◶⟨0,¯1⊸⊑⟩¨𝕨 # Minimum shape required by extra elements 𝕨↩dr(⥊¯1⊸↓⍟⊣)¨𝕨 # Remove extra elements; deshape components s⌈↩1+¯1⌈´¨𝕨 # Result shape 𝕩↩((≠¨𝕨)∾n↓≢𝕩)⥊𝕩 # Merge axes of 𝕩 that are handled together in 𝕨 (𝕨⊸=/𝕩˙)¨↕s # Construct each result group by filtering } GroupInds←{ ! 1==𝕩 𝕩 ⊔ ↕ (1<≡)◶≠‿(∾≢¨) 𝕩 } # Searching: use all-pairs comparisons IndexOf←{ c←1-˜=𝕨 ! 0≤c ! c≤=𝕩 𝕨 ∧○(0<≠)⟜⥊◶⟨0⥊˜c-⊸↓≢∘⊢, (+˝∧`)≢⎉c⎉c‿∞⟩ ToArray 𝕩 } MarkFirst←{ ! 1≤=𝕩 u←0↑𝕩 (0<≠)◶⟨⟨⟩,{⊑𝕩∊u}◶{u∾↩𝕩⋄1}‿0˘⟩𝕩 } Find←{ r←=𝕨 ! r≤=𝕩 𝕨 ≡⎉r ((1+r-⊸↑≢𝕩)⌊≢𝕨)⊸↕⎉r 𝕩 }○ToArray ReorderAxes←{ 𝕩<⍟(0=≡)↩ ! 1≥=𝕨 𝕨⥊↩ ! (≠𝕨)≤=𝕩 ! ∧´Nat¨𝕨 r←(=𝕩)-+´¬∊𝕨 # Result rank: duplicates in 𝕨 reduce it ! ∧´𝕨-<)𝕩 }‿{ # At least one array e←𝕨-˜○(∨´0=≢)𝕩 𝕨(e=0)◶e‿{ # Backup ordering by rank c←𝕨×∘-○(IsArray+=)𝕩 # l is the number of elements to compare # Also update backup ordering c if lengths differ s←≢𝕨 ⋄ t←≢𝕩 ⋄ r←𝕨⌊○=𝕩 l←s{i←+´∧`𝕨=𝕩⋄m←×´i↑𝕨⋄{c↩×-´𝕩⋄m×↩⌊´𝕩}∘(⊑¨⟜𝕨‿𝕩)⍟(r⊸>)i⋄m}○(r↑⌽)t # Compare elements, stopping at the first difference a←⥊𝕨⋄b←⥊𝕩 Trav←(=⟜l)◶{Trav∘(1+𝕩)⍟(0⊸=)a Cmp○(𝕩⊸⊑)b}‿c Trav 0 }𝕩 } _grade←{ ! 1≤=𝕩 # Compare all cells and break ties with indices # Then sum and invert as a permutation i⊐˜+´˘(𝔽⎉∞‿¯1⎉¯1‿∞˜𝕩)(⌈⟜0+=⟜0⊸×)>⌜˜i←↕≠𝕩 } _bins←{ c←1-˜=𝕨 # Rank of compared cells ! 0≤c ! c≤=𝕩 LE←𝔽⎉c≤0˙ # Does 𝕨 precede 𝕩? ! (0<≠)◶⟨1,∧´·LE˝˘2↕⊢⟩𝕨 # 𝕨 must be ordered 𝕨 (0<≠𝕨)◶⟨0⎉c∘⊢,+˝LE⎉¯1‿∞⟩ 𝕩 # Number of 𝕨 cells preceding or matching each 𝕩 cell } # ⊐˜ ranks without breaking ties and ⍋∘⍋ breaks ties, so the # difference after adjusting ⊐˜ is the occurrence count OccurrenceCount ← ⊐˜(⊢-⊏)⍋∘⍋ # Tag each cell with an occurrence count, then search ProgressiveIndexOf ← {𝕨⊐○(((≢∾2˙)⥊≍˘⟜OccurrenceCount∘⥊)𝕨⊸⊐)𝕩}