Skip to content

Commit 0cf2c92

Browse files
committed
add subscripted bits
1 parent 9c53244 commit 0cf2c92

File tree

10 files changed

+93
-48
lines changed

10 files changed

+93
-48
lines changed

changelog.md

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This version is not yet released. If you are reading this on the website, then t
1414
- Change [`backward ˜`](https://uiua.org/docs/backward)'s glyph back. `𝄈` will continue to work and will be formatted as `˜`.
1515
- This glyph has much better font support
1616
- Stabilize [`backward ˜`](https://uiua.org/docs/backward)
17+
- Add subscripts for [`bits ⋯`](https://uiua.org/docs/bits) to force the number of bits
1718
- Add sided subscripts for [`reach 𝄐`](https://uiua.org/docs/reach)
1819
- Add the [`# External!`](https://www.uiua.org/tutorial/documentation#external) semantic comment to mark functions that are provided via Rust code
1920
- These functions don't require a Uiua implementation and will show up in the LSP

site/src/other.rs

+1
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,7 @@ pub fn Subscripts() -> impl IntoView {
516516
subscript(Ceil, "To N decimal places", "# Experimental!\n⌈₄ π\n⌈₄ τ"),
517517
subscript(First, "First N values", "⊢₂ \"hello\""),
518518
subscript(Last, "Last N values", "⊣₂ \"hello\""),
519+
subscript(Bits, "Force N bits", "⋯₄ [1 2 3]"),
519520
subscript(Rand, "Random integer", "⚂₁₀₀"),
520521
subscript(On, "First N values", "{⟜₂[⊙⊙∘] 1 2 3}"),
521522
subscript(By, "Last N values", "{⊸₂[⊙⊙∘] 1 2 3}"),

src/algorithm/monadic.rs

+40-27
Original file line numberDiff line numberDiff line change
@@ -1307,10 +1307,10 @@ impl<T: ArrayValue> Array<T> {
13071307

13081308
impl Value {
13091309
/// Encode the `bits` of the value
1310-
pub fn bits(&self, env: &Uiua) -> UiuaResult<Value> {
1310+
pub fn bits(&self, count: Option<usize>, env: &Uiua) -> UiuaResult<Value> {
13111311
match self {
1312-
Value::Byte(n) => n.bits(env),
1313-
Value::Num(n) => n.bits(env),
1312+
Value::Byte(n) => n.bits(count, env),
1313+
Value::Num(n) => n.bits(count, env),
13141314
_ => Err(env.error("Argument to bits must be an array of natural numbers")),
13151315
}
13161316
}
@@ -1328,19 +1328,24 @@ impl Value {
13281328
.pop()
13291329
.unwrap_or(0);
13301330
match self {
1331-
Value::Byte(n) => n.bits_impl(min_bits_len, env),
1332-
Value::Num(n) => n.bits_impl(min_bits_len, env),
1331+
Value::Byte(n) => n.bits_impl(min_bits_len, None, env),
1332+
Value::Num(n) => n.bits_impl(min_bits_len, None, env),
13331333
_ => Err(env.error("Argument to undo un bits must be an array of integers")),
13341334
}
13351335
}
13361336
}
13371337

13381338
impl<T: RealArrayValue> Array<T> {
13391339
/// Encode the `bits` of the array
1340-
pub fn bits(&self, env: &Uiua) -> UiuaResult<Value> {
1341-
self.bits_impl(0, env)
1340+
pub fn bits(&self, count: Option<usize>, env: &Uiua) -> UiuaResult<Value> {
1341+
self.bits_impl(0, count, env)
13421342
}
1343-
fn bits_impl(&self, min_bits_len: usize, env: &Uiua) -> UiuaResult<Value> {
1343+
fn bits_impl(
1344+
&self,
1345+
min_bits_len: usize,
1346+
count: Option<usize>,
1347+
env: &Uiua,
1348+
) -> UiuaResult<Value> {
13441349
let mut nats = Vec::with_capacity(self.data.len());
13451350
let mut negatives = Vec::with_capacity(self.data.len());
13461351
let mut any_neg = false;
@@ -1357,33 +1362,41 @@ impl<T: RealArrayValue> Array<T> {
13571362
Primitive::Bits.format()
13581363
)));
13591364
}
1360-
nats.push(n.abs().round() as u128);
1365+
let mut nat = n.abs().round() as u128;
1366+
if let Some(count) = count {
1367+
nat &= (1 << count) - 1;
1368+
}
1369+
nats.push(nat);
13611370
negatives.push(n < 0.0);
13621371
any_neg |= n < 0.0;
13631372
}
1364-
let mut max = if let Some(max) = nats.iter().max() {
1365-
*max
1373+
let bit_count = if let Some(count) = count {
1374+
count
13661375
} else {
1367-
let mut shape = self.shape.clone();
1368-
shape.push(0);
1369-
return Ok(Array::<u8>::new(shape, CowSlice::new()).into());
1376+
let mut max = if let Some(max) = nats.iter().max() {
1377+
*max
1378+
} else {
1379+
let mut shape = self.shape.clone();
1380+
shape.push(0);
1381+
return Ok(Array::<u8>::new(shape, CowSlice::new()).into());
1382+
};
1383+
let mut max_bits = 0;
1384+
while max != 0 {
1385+
max_bits += 1;
1386+
max >>= 1;
1387+
}
1388+
max_bits.max(min_bits_len)
13701389
};
1371-
let mut max_bits = 0;
1372-
while max != 0 {
1373-
max_bits += 1;
1374-
max >>= 1;
1375-
}
1376-
max_bits = max_bits.max(min_bits_len);
13771390
let mut shape = self.shape.clone();
1378-
shape.push(max_bits);
1391+
shape.push(bit_count);
13791392
let val: Value = if any_neg {
13801393
// If any number is negative, make a f64 array
1381-
let mut new_data = eco_vec![0.0; self.data.len() * max_bits];
1394+
let mut new_data = eco_vec![0.0; self.data.len() * bit_count];
13821395
let new_data_slice = new_data.make_mut();
13831396
// LSB first
13841397
for (i, (n, is_neg)) in nats.into_iter().zip(negatives).enumerate() {
1385-
for j in 0..max_bits {
1386-
let index = i * max_bits + j;
1398+
for j in 0..bit_count {
1399+
let index = i * bit_count + j;
13871400
new_data_slice[index] = u8::from(n & (1 << j) != 0) as f64;
13881401
if is_neg {
13891402
new_data_slice[index] = -new_data_slice[index];
@@ -1393,12 +1406,12 @@ impl<T: RealArrayValue> Array<T> {
13931406
Array::new(shape, new_data).into()
13941407
} else {
13951408
// If all numbers are natural, make a u8 array
1396-
let mut new_data = eco_vec![0; self.data.len() * max_bits];
1409+
let mut new_data = eco_vec![0; self.data.len() * bit_count];
13971410
let new_data_slice = new_data.make_mut();
13981411
// LSB first
13991412
for (i, n) in nats.into_iter().enumerate() {
1400-
for j in 0..max_bits {
1401-
let index = i * max_bits + j;
1413+
for j in 0..bit_count {
1414+
let index = i * bit_count + j;
14021415
new_data_slice[index] = u8::from(n & (1 << j) != 0);
14031416
}
14041417
}

src/compile/invert/un.rs

+5
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ pub static UN_PATTERNS: &[&dyn InvertPattern] = &[
213213
&RepeatPat,
214214
&DupPat,
215215
&DumpPat,
216+
&NBitsPat,
216217
&(Sqrt, (Dup, Mul)),
217218
&(Select, (Dup, Len, Range)),
218219
&(Pick, (Dup, Shape, Range)),
@@ -970,6 +971,10 @@ inverse!(GetLocalPat, input, asm, GetLocal { def, span }, {
970971
Ok((&[], inv))
971972
});
972973

974+
inverse!(NBitsPat, input, _, ImplPrim(NBits(_), span), {
975+
Ok((input, ImplPrim(UnBits, span)))
976+
});
977+
973978
#[derive(Debug)]
974979
struct Trivial;
975980
impl InvertPattern for Trivial {

src/compile/invert/under.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ static UNDER_PATTERNS: &[&dyn UnderPattern] = &[
219219
&(
220220
UnBits,
221221
(Dup, Shape, PushUnd(1), UnBits),
222-
(PopUnd(1), UndoUnbits),
222+
(PopUnd(1), UndoUnBits),
223223
),
224224
// Rounding
225225
&(

src/compile/mod.rs

+16-10
Original file line numberDiff line numberDiff line change
@@ -2156,33 +2156,29 @@ code:
21562156
1 => self.primitive(Fix, span),
21572157
2 => self.primitive(Couple, span),
21582158
n => Node::Array {
2159-
len: ArrayLen::Static(self.positive_subscript(
2160-
n,
2161-
Couple,
2162-
span.clone(),
2163-
)?),
2159+
len: ArrayLen::Static(self.positive_subscript(n, Couple, &span)?),
21642160
inner: Node::empty().into(),
21652161
boxed: false,
21662162
prim: Some(Couple),
21672163
span: self.add_span(span),
21682164
},
21692165
},
21702166
Box => Node::Array {
2171-
len: ArrayLen::Static(self.positive_subscript(n, Box, span.clone())?),
2167+
len: ArrayLen::Static(self.positive_subscript(n, Box, &span)?),
21722168
inner: Node::empty().into(),
21732169
boxed: true,
21742170
prim: Some(Box),
21752171
span: self.add_span(span),
21762172
},
21772173
Stack => Node::ImplPrim(
21782174
ImplPrimitive::StackN {
2179-
n: self.positive_subscript(n, Stack, span.clone())?,
2175+
n: self.positive_subscript(n, Stack, &span)?,
21802176
inverse: false,
21812177
},
21822178
self.add_span(span),
21832179
),
21842180
First | Last => {
2185-
let n = self.positive_subscript(n, prim, span.clone())?;
2181+
let n = self.positive_subscript(n, prim, &span)?;
21862182
let span = self.add_span(span);
21872183
match n {
21882184
0 => Node::Prim(Pop, span),
@@ -2210,6 +2206,11 @@ code:
22102206
]),
22112207
}
22122208
}
2209+
Bits => {
2210+
let n = self.positive_subscript(n, Bits, &span)?;
2211+
let span = self.add_span(span);
2212+
Node::ImplPrim(ImplPrimitive::NBits(n), span)
2213+
}
22132214
_ => {
22142215
self.add_error(
22152216
span.clone(),
@@ -2312,10 +2313,15 @@ impl Compiler {
23122313
SubNOrSide::Side(side) => Some(n_or_side.span.sp(side)),
23132314
}
23142315
}
2315-
fn positive_subscript(&mut self, n: i32, prim: Primitive, span: CodeSpan) -> UiuaResult<usize> {
2316+
fn positive_subscript(
2317+
&mut self,
2318+
n: i32,
2319+
prim: Primitive,
2320+
span: &CodeSpan,
2321+
) -> UiuaResult<usize> {
23162322
if n < 0 {
23172323
self.add_error(
2318-
span,
2324+
span.clone(),
23192325
format!("Subscript for {} must be positive", prim.format()),
23202326
);
23212327
}

src/compile/modifier.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ impl Compiler {
455455
.and_then(|sub| self.subscript_n(sub, On))
456456
.filter(|n| n.value > 1)
457457
{
458-
let n = self.positive_subscript(sub.value, On, sub.span)?;
458+
let n = self.positive_subscript(sub.value, On, &sub.span)?;
459459
Node::ImplMod(ImplPrimitive::OnSub(n), eco_vec![sn], span)
460460
} else {
461461
let prim = if sn.sig.args == 0 { Dip } else { On };
@@ -469,7 +469,7 @@ impl Compiler {
469469
.and_then(|sub| self.subscript_n(sub, By))
470470
.filter(|n| n.value > 1)
471471
{
472-
let n = self.positive_subscript(sub.value, By, sub.span)?;
472+
let n = self.positive_subscript(sub.value, By, &sub.span)?;
473473
if n == sn.sig.args {
474474
self.emit_diagnostic(
475475
format!(
@@ -544,7 +544,7 @@ impl Compiler {
544544
let span = self.add_span(modified.modifier.span.clone());
545545
match n.value {
546546
SubNOrSide::N(n) => {
547-
let n = self.positive_subscript(n, Both, modified.modifier.span.clone())?;
547+
let n = self.positive_subscript(n, Both, &modified.modifier.span)?;
548548
self.monadic_modifier_op(modified)?.0.on_all(n, span)
549549
}
550550
SubNOrSide::Side(side) => {
@@ -641,7 +641,7 @@ impl Compiler {
641641
.and_then(|sub| self.subscript_n(sub, prim))
642642
.filter(|n| n.value > 1)
643643
{
644-
let n = self.positive_subscript(sub.value, prim, sub.span)?;
644+
let n = self.positive_subscript(sub.value, prim, &sub.span)?;
645645
let prim = if prim == Off {
646646
if n == sig.args {
647647
self.emit_diagnostic(
@@ -1084,8 +1084,7 @@ impl Compiler {
10841084
{
10851085
match n.value {
10861086
SubNOrSide::N(n) => {
1087-
let n =
1088-
self.positive_subscript(n, prim, modified.modifier.span.clone())?;
1087+
let n = self.positive_subscript(n, prim, &modified.modifier.span)?;
10891088
if n == 0 {
10901089
sn.node
10911090
} else {

src/primitive/defs.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,11 @@ primitive!(
635635
///
636636
/// [under][bits] can be used to perform bit-wise operations.
637637
/// ex: ⍜⋯(¬⬚0↙8) 5
638+
///
639+
/// Subscripted [bits] forces the number of bits to be used. This extends or truncates the bits.
640+
/// ex: ⋯₄ [1 2 3]
641+
/// ex: ⋯ 1234
642+
/// : ⋯₈ 1234
638643
(1, Bits, MonadicArray, ("bits", '⋯')),
639644
/// Rotate the shape of an array
640645
///
@@ -3250,6 +3255,7 @@ macro_rules! impl_primitive {
32503255
BySub(usize),
32513256
WithSub(usize),
32523257
OffSub(usize),
3258+
NBits(usize),
32533259
/// Push the maximum row count of N values
32543260
MaxRowCount(usize),
32553261
}
@@ -3267,6 +3273,7 @@ macro_rules! impl_primitive {
32673273
ImplPrimitive::ReduceDepth(_) => 1,
32683274
ImplPrimitive::StackN { n, .. } => *n,
32693275
ImplPrimitive::MaxRowCount(n) => *n,
3276+
ImplPrimitive::NBits(_) => 1,
32703277
_ => return None
32713278
})
32723279
}
@@ -3278,6 +3285,7 @@ macro_rules! impl_primitive {
32783285
ImplPrimitive::UndoRotate(n) => *n,
32793286
ImplPrimitive::StackN { n, .. } => *n,
32803287
ImplPrimitive::MaxRowCount(n) => *n + 1,
3288+
ImplPrimitive::NBits(_) => 1,
32813289
_ if self.modifier_args().is_some() => return None,
32823290
_ => 1
32833291
})
@@ -3357,7 +3365,7 @@ impl_primitive!(
33573365
(1(0), UnClip, Mutating),
33583366
// Unders
33593367
(1, UndoFix),
3360-
(2, UndoUnbits),
3368+
(2, UndoUnBits),
33613369
(2, AntiBase),
33623370
(3, UndoSelect),
33633371
(3, UndoPick),

src/primitive/mod.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ impl fmt::Display for ImplPrimitive {
196196
write!(f, "{Off}")?;
197197
fmt_subscript(f, *i as i32)
198198
}
199+
NBits(n) => {
200+
write!(f, "{Bits}")?;
201+
fmt_subscript(f, *n as i32)
202+
}
199203
Root => write!(f, "{Anti}{Pow}"),
200204
Cos => write!(f, "cos"),
201205
Asin => write!(f, "{Un}{Sin}"),
@@ -240,7 +244,7 @@ impl fmt::Display for ImplPrimitive {
240244
UnRawMode => write!(f, "{Un}{}", Primitive::Sys(SysOp::RawMode)),
241245
UnClip => write!(f, "{Un}{}", Primitive::Sys(SysOp::Clip)),
242246
ProgressiveIndexOf => write!(f, "{Un}{By}{Select}"),
243-
UndoUnbits => write!(f, "{Under}{Un}{Bits}"),
247+
UndoUnBits => write!(f, "{Under}{Un}{Bits}"),
244248
AntiBase => write!(f, "{Anti}{Base}"),
245249
UndoReverse { n, .. } => write!(f, "{Under}{Reverse}({n})"),
246250
UndoTransposeN(n, _) => write!(f, "{Under}{Transpose}({n})"),
@@ -859,7 +863,7 @@ impl Primitive {
859863
Primitive::Shape => {
860864
env.monadic_ref(|v| v.shape().iter().copied().collect::<Value>())?
861865
}
862-
Primitive::Bits => env.monadic_ref_env(Value::bits)?,
866+
Primitive::Bits => env.monadic_ref_env(|val, env| val.bits(None, env))?,
863867
Primitive::Base => env.dyadic_rr_env(Value::base)?,
864868
Primitive::Reshape => {
865869
let shape = env.pop(1)?;
@@ -1208,6 +1212,11 @@ impl ImplPrimitive {
12081212
ImplPrimitive::DeshapeSub(i) => {
12091213
env.monadic_mut_env(|val, env| val.deshape_sub(*i, true, env))?
12101214
}
1215+
ImplPrimitive::NBits(n) => {
1216+
let val = env.pop(1)?;
1217+
let bits = val.bits(Some(*n), env)?;
1218+
env.push(bits);
1219+
}
12111220
ImplPrimitive::Root => env.dyadic_oo_env(Value::root)?,
12121221
ImplPrimitive::Cos => env.monadic_env(Value::cos)?,
12131222
ImplPrimitive::Asin => env.monadic_env(Value::asin)?,
@@ -1357,7 +1366,7 @@ impl ImplPrimitive {
13571366
}
13581367
ImplPrimitive::MatrixDiv => env.dyadic_rr_env(Value::matrix_div)?,
13591368
// Unders
1360-
ImplPrimitive::UndoUnbits => {
1369+
ImplPrimitive::UndoUnBits => {
13611370
let orig_shape = env.pop(1)?;
13621371
let val = env.pop(2)?;
13631372
env.push(val.undo_un_bits(&orig_shape, env)?);

tests/monadic.ua

+3
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ A ← [[1_2 1_2] [3_3 1_2]]
118118
⍤⟜≍ ⟜⍜°⋯∘ []
119119
⍤⟜≍ ⟜⍜°⋯∘ [[]]
120120

121+
⍤⤙≍ 13 °⋯₃ [1 0 1 1]
122+
⍤⤙≍ [1 0 1 0 0 0 0 0] ⋯₈ 5
123+
121124
# Json
122125
⍤⤙≍ "[1,2,3]" json [1 2 3]
123126
⍤⤙≍ "[\"some\",\"words\"]" json {"some" "words"}

0 commit comments

Comments
 (0)