diff --git a/changelog.md b/changelog.md index 4c50f0ef..f932ea7e 100644 --- a/changelog.md +++ b/changelog.md @@ -18,6 +18,7 @@ This version is not yet released. If you are reading this on the website, then t - [`join ⊂`](https://uiua.org/docs/join) will rank differences greater than 1 can now extend the smaller array - [`un °`](https://uiua.org/docs/un) [`join ⊂`](https://uiua.org/docs/join) is now easier to combine with other inverses - [`un °`](https://uiua.org/docs/un) [`shape △`](https://uiua.org/docs/shape) now generates an array with the given shape and incrementing elements +- Add the experimental [`orient`](https://uiua.org/docs/orient) function, which arranges an array's axes in a specified order - Add the experimental [`fft`](https://uiua.org/docs/fft) function, which performs the Fast Fourier transform - The inverse FFT is also supported via [`un °`](https://uiua.org/docs/un) - Add the experimental [`astar`](https://uiua.org/docs/astar) modifier, which performs the A* pathfinding algorithm diff --git a/site/primitives.json b/site/primitives.json index d1e2044f..8a727e50 100644 --- a/site/primitives.json +++ b/site/primitives.json @@ -877,6 +877,13 @@ "class": "Stack", "description": "Call a function but keep its first argument on the top of the stack" }, + "orient": { + "args": 2, + "outputs": 1, + "class": "DyadicArray", + "description": "Change the order of the axes of an array", + "experimental": true + }, "over": { "glyph": ",", "args": 2, diff --git a/src/algorithm/dyadic/mod.rs b/src/algorithm/dyadic/mod.rs index 96bfca73..b4cd7d70 100644 --- a/src/algorithm/dyadic/mod.rs +++ b/src/algorithm/dyadic/mod.rs @@ -1696,3 +1696,55 @@ impl Array { Ok(Array::new(result_shape, result_data)) } } + +impl Value { + /// `orient` a value by this value + pub fn orient(&self, target: &mut Self, env: &Uiua) -> UiuaResult { + let indices = self.as_ints(env, "Orient indices must be integers")?; + let mut undices = Vec::with_capacity(indices.len()); + for i in indices { + let u = i.unsigned_abs(); + if u >= target.rank() { + return Err(env.error(format!( + "Cannot orient axis {i} in array of rank {}", + target.rank() + ))); + } + if i >= 0 { + undices.push(u); + } else { + undices.push(target.rank() - u); + } + } + if undices.len() > target.rank() { + return Err(env.error(format!( + "Cannot orient array of rank {} with {} indices", + target.rank(), + undices.len() + ))); + } + for (i, u) in undices.iter().enumerate() { + if undices[i + 1..].iter().any(|u2| u == u2) { + return Err(env.error("Orient indices must be unique")); + } + } + let mut orientation: Vec = (0..target.rank()).collect(); + let mut depth_rotations: Vec<(usize, i32)> = Vec::new(); + for (i, &u) in undices.iter().enumerate() { + let j = orientation.iter().position(|&o| o == u).unwrap(); + if i == j { + continue; + } + if j != undices.len() - 1 { + orientation[j..].rotate_left(1); + depth_rotations.push((j, 1)); + } + orientation[i..].rotate_right(1); + depth_rotations.push((i, -1)); + } + for (depth, amnt) in depth_rotations { + target.transpose_depth(depth, amnt); + } + Ok(()) + } +} diff --git a/src/primitive/defs.rs b/src/primitive/defs.rs index de4fb2a7..4264d583 100644 --- a/src/primitive/defs.rs +++ b/src/primitive/defs.rs @@ -762,6 +762,9 @@ primitive!( /// ex: △1_2_3 /// ex: △[1_2 3_4 5_6] /// + /// [un][shape] creates an array of incrementing elements with the given shape. + /// ex: °△ 2_3_4 + /// /// It is a triangle`△` because a triangle is a shape. (1, Shape, MonadicArray, ("shape", '△')), /// Make an array of all natural numbers less than a number @@ -1236,6 +1239,22 @@ primitive!( /// ex: ↻1 □[1 2 3 4] /// ex: ≡↻1 {1_2_3 4_5_6} (2, Rotate, DyadicArray, ("rotate", '↻')), + /// Change the order of the axes of an array + /// + /// The first argument is a list of unique axis indices. + /// The corresponding axes of the array will be moved to the front of the array's shape. + /// Positive indices start from the leading axis. Negative indices start from the trailing axis. + /// ex: # Experimental! + /// : °△ 2_3_4 + /// : orient 1 . + /// ex: # Experimental! + /// : △ orient 2_1 °△ 2_3_4_5 + /// [orient]`¯1` is equivalent to [un][transpose]. + /// ex: # Experimental! + /// : °△ 2_3_4 + /// : ∩△ ⊃°⍉(orient¯1) + /// Currently, all uses of [orient] can be written with sequences of [transpose] and [rows]. + (2, Orient, DyadicArray, "orient"), /// The n-wise windows of an array /// /// ex: ◫2 .⇡4 diff --git a/src/primitive/mod.rs b/src/primitive/mod.rs index ac8b055e..1dc411cc 100644 --- a/src/primitive/mod.rs +++ b/src/primitive/mod.rs @@ -393,7 +393,7 @@ impl Primitive { use SysOp::*; matches!( self, - But | (Coordinate | Astar | Fft | Triangle | Case) + But | (Orient | Coordinate | Astar | Fft | Triangle | Case) | Sys(Ffi | MemCopy | MemFree | TlsListen) | (Stringify | Quote | Sig) ) @@ -574,6 +574,10 @@ impl Primitive { Primitive::Take => env.dyadic_oo_env(Value::take)?, Primitive::Drop => env.dyadic_oo_env(Value::drop)?, Primitive::Rotate => env.dyadic_ro_env(Value::rotate)?, + Primitive::Orient => env.dyadic_ro_env(|a, mut b, env| { + a.orient(&mut b, env)?; + Ok(b) + })?, Primitive::Couple => env.dyadic_oo_env(Value::couple)?, Primitive::Rise => env.monadic_ref(Value::rise)?, Primitive::Fall => env.monadic_ref(Value::fall)?, diff --git a/tests/units.ua b/tests/units.ua index 688c3ddc..e6e30ffc 100644 --- a/tests/units.ua +++ b/tests/units.ua @@ -631,3 +631,9 @@ repr[{[[1 2][2 3]][3 4]}{[[1 2][2 3]]□{[[1 2][2 3]]□□[3 4]}}] ⍤⟜≍: [5 8] [⫯+ 3 5] ⍤⟜≍: [4 1 2 3 4] [⫯⊙⊙⊙∘ 1 2 3 4] ⍤⟜≍: [4 1 2 3] [⫯⊙⊙⊙◌ 1 2 3 4] + +# Orient +⍤⟜≍: °⍉⟜(orient¯1) °△ 2_3_4 +⍤⟜≍: °⍉⟜(orient2) °△ 2_3_4 +⍤⟜≍: ≡⍉⟜(orient0_2) °△ 2_3_4 +⍤⟜≍: ≡°⍉≡≡⍉°⍉≡≡⍉⟜(orient2_1) °△ 2_3_4_5 \ No newline at end of file