diff --git a/.gitignore b/.gitignore index ea8c4bf..e7d4aa4 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /target +ngn +.aider* diff --git a/Cargo.lock b/Cargo.lock index 77db26e..5c72b1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" diff --git a/bench-vs-ngn-k.sh b/bench-vs-ngn-k.sh new file mode 100755 index 0000000..7ca09d3 --- /dev/null +++ b/bench-vs-ngn-k.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env sh + +# ngn/k +mkdir -p ngn +git clone https://codeberg.org/ngn/k ./ngn/k || true +pushd ./ngn/k +git pull +make k +popd + + +# rok +cargo build --release + +# 8GB of i64 +B="\\\t #!1000000000" +echo "ngn/k time: $B" +echo "$B" | ./ngn/k/k +echo "rok time: $B" +echo "$B" | ./target/release/rok + +# 8GB of i64 +B="\\\t #2*!1000000000" +echo "ngn/k time: $B" +echo "$B" | ./ngn/k/k +echo "rok time: $B" +echo "$B" | ./target/release/rok + +B="\\\t +/2*!1000000000" +echo "ngn/k time: $B" +echo "$B" | ./ngn/k/k +echo "rok time: $B" +echo "$B" | ./target/release/rok diff --git a/src/lib.rs b/src/lib.rs index c2148bc..1103035 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,8 +4,9 @@ use log::debug; use polars::prelude::*; use std::cmp::Ordering; use std::collections::HashMap; -use std::fmt; +use std::{f64, fmt}; use std::fs::File; +use std::hash::{Hash, Hasher}; use std::iter::zip; use std::path::Path; use std::{collections::VecDeque, iter::repeat, ops}; @@ -31,21 +32,25 @@ pub enum K { Char(char), Symbol(String), SymbolArray(Series), - BoolArray(Series), - IntArray(Series), - // Int8Array(Series), // TODO maybe later? - // Int16Array(Series), // TODO maybe later? - // Int128Array(Series), // TODO maybe later? - FloatArray(Series), + BoolArray(Series), // Series // TODO should this be Series? bitstrings? + IntArray(Series), // Series + FloatArray(Series), // Series CharArray(String), Nil, // Is Nil a noun? List(Vec), + // Dictionary(IndexMap), //TODO dicts should be K: K Dictionary(IndexMap), Table(DataFrame), //Quote(Box) // Is Quote a noun? Name(String), + // Int8Array(Series), Int16Array(Series), Int32Array(Series), Int128Array(Series), // TODO maybe later? + // Float32Array(Series), // TODO maybe later? + // Decimal(_), DecimalArray(Series) // TODO maybe later? } -// impl Eq for K {} // TODO for the v_unique() K::List() case + +// Needed for the v_unique() and v_group() K::List() case. +// TODO Is this actually ok even though Series and DataFrame don't impl Eq? +impl Eq for K {} #[derive(Clone, Debug, PartialEq)] pub enum KW /* KWords */ { @@ -87,6 +92,7 @@ impl K { CharArray(a) => a.len(), Dictionary(d) => d.len(), List(v) => v.len(), + Table(df) => df.height(), _ => 1, } } @@ -97,8 +103,8 @@ impl K { Nil => Ok(Nil.clone()), SymbolArray(_) | Symbol(_) => Ok(Symbol("".to_string())), BoolArray(_) | Bool(_) => Ok(Bool(0)), - IntArray(_) | Int(_) => Ok(Int(Some(0))), - FloatArray(_) | Float(_) => Ok(Float(0.0)), + IntArray(_) | Int(_) => Ok(Int(None)), + FloatArray(_) | Float(_) => Ok(K::Float(f64::NAN)), CharArray(_) | Char(_) => Ok(Char(' ')), Dictionary(_) => Err("type"), // TODO right? Table(_) => Err("type"), // TODO right? @@ -186,6 +192,8 @@ impl fmt::Display for K { } K::BoolArray(b) => { let s = b + .cast(&DataType::Boolean) + .unwrap() .bool() .unwrap() .into_iter() @@ -202,7 +210,11 @@ impl fmt::Display for K { } else { strip_quotes(s[..(cols - 3)].to_string() + "..") }; - write!(f, "{}b", s) + if b.len() == 1 { + write!(f, ",{}", s) + } else { + write!(f, "{}b", s) + } } K::IntArray(b) => { let max_n = cols / 2; // ints take min 2 chars, we could fit this many max on a row @@ -218,7 +230,13 @@ impl fmt::Display for K { .collect(), ); let s = if s.len() < cols { s } else { s[..(cols - 2)].to_string() + ".." }; - write!(f, "{}", s) + if b.len() == 0 { + write!(f, "!0") + } else if b.len() == 1 { + write!(f, ",{}", s) + } else { + write!(f, "{}", s) + } } K::FloatArray(b) => { let max_n = cols / 2; // floats also take min 2 chars, we could fit this many max on a row @@ -234,20 +252,13 @@ impl fmt::Display for K { .collect(), ); let s = if s.len() < cols { s } else { s[..(cols - 2)].to_string() + ".." }; - write!(f, "{}", s) + if b.len() == 1 { + write!(f, ",{}", s) + } else { + write!(f, "{}", s) + } } K::CharArray(ca) => { - // let s: String = ca - // .str() - // .unwrap() - // .into_iter() - // .take(cols - 2) - // .map(|u| match u { - // Some(u) => u, - // None => panic!("impossible"), - // }) - // .collect(); - // // let s: String = ca.str().unwrap().to_string(); let s = ca; if s.len() < (cols - 2) { @@ -296,6 +307,47 @@ impl fmt::Display for K { } } +impl Hash for K { + fn hash(&self, state: &mut H) { + match self { + K::Bool(b) => b.hash(state), + K::Int(i) => i.hash(state), + K::Float(f) => f.to_string().hash(state), + K::Char(c) => c.hash(state), + K::Symbol(s) => s.hash(state), + K::BoolArray(b) => { + let v: Vec> = b.u8().unwrap().into_iter().collect(); + v.hash(state) + } + K::IntArray(i) => { + let v: Vec> = i.i64().unwrap().into_iter().collect(); + v.hash(state) + } + K::FloatArray(f) => { + let v: Vec> = f + .f64() + .unwrap() + .into_iter() + .map(|f| match f { + Some(f) => Some(f.to_string()), + None => None, + }) + .collect(); + v.hash(state) + } + K::CharArray(c) => c.hash(state), + K::SymbolArray(_s) => todo!("impl hash for K::SymbolArray"), + + K::List(v) => v.hash(state), + K::Dictionary(_d) => todo!("impl hash for K::Dictionary"), + K::Table(_df) => todo!("impl hash for K::Table"), + + K::Nil => self.hash(state), + K::Name(_) => panic!("impossible"), + } + } +} + impl From for K { fn from(item: String) -> Self { K::CharArray(item) } } @@ -304,18 +356,57 @@ impl TryFrom for K { type Error = &'static str; fn try_from(s: Series) -> Result { - if s.i64().is_ok() { + if s.i64().is_ok() || s.i32().is_ok() || s.u64().is_ok() || s.u32().is_ok() || s.u8().is_ok() { if s.min().unwrap() == Some(0) && s.max().unwrap() == Some(1) { - Ok(K::BoolArray(s.cast(&DataType::UInt8).unwrap())) + // Ok(K::BoolArray(s.cast(&DataType::UInt8).unwrap())) + Ok(K::BoolArray(s.cast(&DataType::Boolean).unwrap())) } else { - Ok(K::IntArray(s)) + Ok(K::IntArray(s.cast(&DataType::Int64).unwrap())) } } else if s.f64().is_ok() { Ok(K::FloatArray(s)) } else if s.categorical().is_ok() { Ok(K::SymbolArray(s)) + } else if s.str().is_ok() { + Ok(K::List( + s.iter() + .map(|s| { + // TODO s.to_string() adds surrounding " chars + K::CharArray(s.to_string()) + }) + .collect(), + )) } else { - Err("type") + todo!("try_from() nyi: {}", s); + // Err("type") + } + } +} + +impl TryFrom> for K { + type Error = &'static str; + + fn try_from(v: AnyValue) -> Result { + match v { + // AnyValue::Boolean(b) => Ok(K::Bool(match b { + // true => 1, + // false => 0, + // })), + AnyValue::Boolean(b) => Ok(K::Bool(b as u8)), + AnyValue::UInt16(i) => Ok(K::Int(Some(i as i64))), + AnyValue::Int16(i) => Ok(K::Int(Some(i as i64))), + AnyValue::UInt32(i) => Ok(K::Int(Some(i as i64))), + AnyValue::Int32(i) => Ok(K::Int(Some(i as i64))), + AnyValue::UInt64(i) => Ok(K::Int(Some(i as i64))), + AnyValue::Int64(i) => Ok(K::Int(Some(i))), + AnyValue::Float32(f) => Ok(K::Float(f as f64)), + AnyValue::Float64(f) => Ok(K::Float(f)), + AnyValue::List(s) => K::try_from(s), + AnyValue::String(s) => Ok(K::CharArray(s.to_string())), + _ => { + println!("try_from() nyi: {}", v); + Err("type") + } } } } @@ -511,7 +602,8 @@ pub fn v_none1(_x: K) -> Result { Err("rank") } pub fn v_none2(_x: K, _y: K) -> Result { Err("rank") } pub fn v_none3(_x: K, _y: K, _z: K) -> Result { Err("rank") } pub fn v_none4(_a: K, _b: K, _c: K, _d: K) -> Result { Err("rank") } -pub fn v_d_none2(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { Err("rank") } +pub fn av_none1(_env: &mut Env, _v: KW, _x: K) -> Result { Err("rank") } +pub fn av_d_none2(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { Err("rank") } type AV1 = fn(&mut Env, KW, K) -> Result; type AV2 = fn(&mut Env, KW, K, K) -> Result; @@ -527,7 +619,7 @@ pub fn primitives_table() -> VerbDispatchTable { ("-", (v_negate, v_negate, v_minus, v_minus, v_minus, v_minus, v_none3, v_none4)), ("*", (v_first, v_first, v_times, v_times, v_times, v_times, v_none3, v_none4)), ("%", (v_sqrt, v_sqrt, v_divide, v_divide, v_divide, v_divide, v_none3, v_none4)), - ("!", (v_iota, v_odometer, v_d_bang, v_d_bang, v_d_bang, v_d_bang, v_none3, v_none4)), + ("!", (v_iota, v_keys_odometer, v_d_bang, v_d_bang, v_d_bang, v_d_bang, v_none3, v_none4)), ("&", (v_where, v_where, v_min, v_min, v_min, v_min, v_none3, v_none4)), ("|", (v_reverse, v_reverse, v_max, v_max, v_max, v_max, v_none3, v_none4)), ("<", (v_asc, v_asc, v_lesser, v_lesser, v_lesser, v_lesser, v_none3, v_none4)), @@ -547,7 +639,7 @@ pub fn primitives_table() -> VerbDispatchTable { ("-:", (v_negate, v_negate, v_none2, v_none2, v_none2, v_none2, v_none3, v_none4)), ("*:", (v_first, v_first, v_none2, v_none2, v_none2, v_none2, v_none3, v_none4)), ("%:", (v_sqrt, v_sqrt, v_none2, v_none2, v_none2, v_none2, v_none3, v_none4)), - ("!:", (v_iota, v_odometer, v_none2, v_none2, v_none2, v_none2, v_none3, v_none4)), + ("!:", (v_iota, v_keys_odometer, v_none2, v_none2, v_none2, v_none2, v_none3, v_none4)), ("&:", (v_where, v_where, v_none2, v_none2, v_none2, v_none2, v_none3, v_none4)), ("|:", (v_reverse, v_reverse, v_none2, v_none2, v_none2, v_none2, v_none3, v_none4)), ("<:", (v_asc, v_asc, v_none2, v_none2, v_none2, v_none2, v_none3, v_none4)), @@ -638,9 +730,9 @@ pub fn adverbs_table() -> IndexMap<&'static str, (AV1, AV2)> { ("'", (v_each as AV1, v_d_each as AV2)), ("/", (a_slash, a_d_slash)), // over fixedpoint for while ("\\", (a_bslash, a_d_bslash)), // scan scan-fixedpoint scan-for scan-while - ("':", (v_eachprior, v_windows)), - ("/:", (v_eachright, v_d_none2)), - ("\\:", (v_eachleft, v_d_none2)), + ("':", (v_eachprior, v_eachprior_d_or_windows)), + ("/:", (av_none1, v_d_eachright)), + ("\\:", (av_none1, v_d_eachleft)), ]) } @@ -710,28 +802,40 @@ pub fn apply_primitive(env: &mut Env, v: &str, l: Option, r: KW) -> Result match adverbs.get(&v[v.len() - 1..]) { - Some((m_a, d_a)) => match (l, r) { - (Some(KW::Noun(l)), KW::Noun(r)) => { - d_a(env, KW::Verb { name: v[..v.len() - 1].to_string() }, l, r).map(KW::Noun) - } - (None, KW::Noun(r)) => { - m_a(env, KW::Verb { name: v[..v.len() - 1].to_string() }, r).map(KW::Noun) - } - _ => todo!("other adverb cases"), - }, - None => todo!("NotYetImplemented {}", v), - }, + None => { + let t = if let Some((m_a, d_a)) = adverbs.get(&v[v.len() - 2..]) { + Some((2, (m_a, d_a))) + } else if let Some((m_a, d_a)) = adverbs.get(&v[v.len() - 1..]) { + Some((1, (m_a, d_a))) + } else { + None + }; + match t { + Some((adv_len, (m_a, d_a))) => match (l, r) { + (Some(KW::Noun(l)), KW::Noun(r)) => { + d_a(env, KW::Verb { name: v[..v.len() - adv_len].to_string() }, l, r).map(KW::Noun) + } + (None, KW::Noun(r)) => { + m_a(env, KW::Verb { name: v[..v.len() - adv_len].to_string() }, r).map(KW::Noun) + } + _ => todo!("other adverb cases"), + }, + None => todo!("NotYetImplemented {}", v), + } + } }, } } pub fn apply_adverb(a: &str, l: KW) -> Result { // Return a new Verb that implements the appropriate adverb behaviour match l { - KW::Verb { name } => match a { - "\'" | "/" | "\\" => Ok(KW::Verb { name: name + a }), - _ => panic!("invalid adverb"), - }, + KW::Verb { name } => { + if adverbs_table().keys().contains(&a) { + Ok(KW::Verb { name: name + a }) + } else { + panic!("invalid adverb") + } + } KW::Function { body, args, adverb: None } => match a { "\'" | "/" | "\\" => Ok(KW::Function { body, args, adverb: Some(a.to_string()) }), _ => panic!("invalid adverb"), @@ -804,20 +908,22 @@ pub fn apply_function(env: &mut Env, f: KW, arg: KW) -> Result /// promote_nouns(l,r) => (l,r) eg. (Int, Bool) => (Int, Int) /// /// Also promotes scalars to arrays to match length of other arg. -/// Eg. promote_nouns(1, [1 2 3]) => ([1 2 3], [1 2 3]). +/// Eg. promote_nouns(1, [1 2 3]) => ([1 1 1], [1 2 3]). +/// TODO: Improve performance by NOT promoting scalars to arrays. /// Similar to promote_num() can we combine these somehow and be more concise? #[rustfmt::skip] pub fn promote_nouns(l: K, r: K) -> (K, K) { match (&l, &r) { (K::Bool(l), K::Int(_)) => (K::Int(Some(*l as i64)), r), (K::Bool(l), K::Float(_)) => (K::Float(*l as f64), r), + (K::Bool(l), K::BoolArray(_)) => (K::BoolArray(repeat(*l).take(r.len()).collect()), r), (K::Bool(l), K::IntArray(_)) => (K::IntArray(repeat(*l as i64).take(r.len()).collect()), r), (K::Bool(l), K::FloatArray(_)) => (K::FloatArray(repeat(*l as f64).take(r.len()).collect()), r), (K::Int(_), K::Bool(r)) => (l, K::Int(Some(*r as i64))), (K::Int(Some(l)), K::Float(_)) => (K::Float(*l as f64), r), - (K::Int(Some(l)), K::BoolArray(r)) => (K::IntArray(repeat(*l as i64).take(r.len()).collect()), K::IntArray(r.cast(&DataType::Int64).unwrap())), - (K::Int(Some(l)), K::IntArray(_)) => (K::IntArray(repeat(*l as i64).take(r.len()).collect()), r), + (K::Int(Some(l)), K::BoolArray(r)) => (K::IntArray(repeat(*l).take(r.len()).collect()), K::IntArray(r.cast(&DataType::Int64).unwrap())), + (K::Int(Some(l)), K::IntArray(_)) => (K::IntArray(repeat(*l).take(r.len()).collect()), r), (K::Int(Some(l)), K::FloatArray(_)) => (K::FloatArray(repeat(*l as f64).take(r.len()).collect()), r), (K::Int(None), K::Float(_)) => (K::Float(f64::NAN), r), (K::Int(None), K::BoolArray(r)) => (K::IntArray(repeat(None::).take(r.len()).collect()), K::IntArray(r.cast(&DataType::Int64).unwrap())), @@ -831,6 +937,12 @@ pub fn promote_nouns(l: K, r: K) -> (K, K) { (K::Float(l), K::IntArray(r)) => {(K::FloatArray(repeat(l).take(r.len()).collect()), K::FloatArray(r.cast(&DataType::Float64).unwrap()))} (K::Float(l), K::FloatArray(_)) => (K::FloatArray(repeat(l).take(r.len()).collect()), r), + // (K::Char(_l), K::Int(_)) => panic!("nyi"), // TODO + // (K::Char(_l), K::Float(_)) => panic!("nyi"), // TODO + (K::Char(l), K::CharArray(_)) => (K::CharArray(repeat(*l).take(r.len()).collect()), r), + // (K::Char(_l), K::IntArray(_)) => panic!("nyi"), // TODO + // (K::Char(_l), K::FloatArray(_)) => panic!("nyi"), // TODO + (K::BoolArray(_), K::Bool(r)) => (l.clone(), K::BoolArray(repeat(*r as f64).take(l.len()).collect())), (K::BoolArray(l), K::Int(Some(r))) => (K::IntArray(l.cast(&DataType::Int64).unwrap()), K::IntArray(repeat(r).take(l.len()).collect())), (K::BoolArray(l), K::Int(None)) => {(K::IntArray(l.cast(&DataType::Int64).unwrap()), K::IntArray(repeat(None::).take(l.len()).collect()))} @@ -871,14 +983,16 @@ pub fn promote_nouns(l: K, r: K) -> (K, K) { v_makedict(K::SymbolArray(Series::new("", l_inner.keys().cloned().collect::>()) .cast(&DataType::Categorical(None, CategoricalOrdering::Lexical)) .unwrap(),), r,) .unwrap()), (_l, K::Dictionary(_)) if !matches!(_l, K::Dictionary(_)) => match (l.len(), r.len()) { (0,0) => todo!("promote_nouns empties"), - (1,_) => todo!("promote_nouns atom"), - (_,1) => todo!("promote_nouns atom"), + // (1,_) => todo!("promote_nouns atom"), + // (_,1) => todo!("promote_nouns atom"), + (1,_) => (l,r), + (_,1) => (l,r), (llen, rlen) => if llen == rlen { promote_nouns(K::List(k_to_vec(l).unwrap()), r) } else { (l,r) } } (K::Dictionary(_), _r) if !matches!(_r, K::Dictionary(_))=> match (l.len(), r.len()) { (0,0) => todo!("promote_nouns empties"), - (1,_) => todo!("promote_nouns atom"), - (_,1) => todo!("promote_nouns atom"), + (1,_) => (l,r), + (_,1) => (l,r), (llen, rlen) => if llen == rlen { promote_nouns(l, K::List(k_to_vec(r).unwrap())) } else { (l,r) } } _ => (l, r), @@ -887,7 +1001,7 @@ pub fn promote_nouns(l: K, r: K) -> (K, K) { macro_rules! impl_op { ($op:tt, $opf:ident, $self:ident, $r:ident) => { - match promote_nouns($self, $r) { + match promote_nouns($self.clone(), $r.clone()) { (K::Bool(l), K::Bool(r)) => K::Int(Some(l as i64 $op r as i64)), (K::Int(Some(l)), K::Int(Some(r))) => K::Int(Some(l as i64 $op r)), (K::Int(None), K::Int(_)) | (K::Int(_), K::Int(None))=> K::Int(None), @@ -899,7 +1013,7 @@ macro_rules! impl_op { (K::Dictionary(_), _) => todo!("dict"), (_, K::Table(_)) => todo!("table"), (K::Table(_), _) => todo!("table"), - _ => todo!("various $op pairs - LOTS MORE to do still: char/dicts/tables/etc"), + _ => todo!("various $op pairs - LOTS MORE to do still: char/dicts/tables/etc. self: {}, r: {}", $self, $r), } }; } @@ -918,14 +1032,26 @@ impl ops::Mul for K { } impl ops::Div for K { type Output = Self; - fn div(self, r: Self) -> Self::Output { impl_op!(/, div, self, r) } + fn div(self, r: Self) -> Self::Output { + //TODO this doesn't gracefully handle divide by zero. in k 0n~0%0 + impl_op!(/, div, self, r) + } } fn len_ok(l: &K, r: &K) -> Result { - if l.len() == r.len() || l.len() == 1 || r.len() == 1 { - Ok(true) - } else { - Err("length") + match (l, r) { + (K::Dictionary(_), K::Dictionary(_)) => Ok(true), + (l @ K::Table(_), r @ K::Table(_)) => match l.len() == r.len() { + true => Ok(true), + false => Err("length"), + }, + _ => { + if l.len() == r.len() || l.len() == 1 || r.len() == 1 { + Ok(true) + } else { + Err("length") + } + } } } @@ -1131,10 +1257,14 @@ pub fn scan_pass1(code: &str) -> Result, &'static str> { ')' => words.push(KW::RP), '{' => words.push(KW::LCB), '}' => words.push(KW::RCB), + // '[' => words.push(KW::LB), // TODO 20241024 handle FuncArgsStart in scan_pass2 + // TODO Remove KW::FuncArgsStart entirely. + // Find a nicer way to parse the difference between: +[2;3] and + [a:2;...] + // ie. Difference between calling a function with arguments, and evaluating an Exprs and then handling it's result '[' if i == 0 => words.push(KW::LB), '[' => match code.chars().nth(i - 1) { Some(c) => { - if " ()[]{".contains(c) { + if " ()[]{;".contains(c) { words.push(KW::LB) } else { words.push(KW::FuncArgsStart) @@ -1355,7 +1485,14 @@ fn scan_function(tokens: Vec) -> Result<(KW, Vec), &'static str> { Some(KW::RCB) => match depth { 0 => { let (args, body) = scan_function_args(tokens[0..i].to_vec())?; - return Ok((KW::Function { body, args, adverb: None }, tokens[i..].to_vec())); + return Ok(( + KW::Function { + body: scan_pass3(scan_pass2(body).unwrap()).unwrap(), + args, + adverb: None, + }, + tokens[i..].to_vec(), + )); } _ => depth -= 1, }, @@ -1392,7 +1529,18 @@ fn scan_function_args(body: Vec) -> Result<(Vec, Vec), &'static } let mut args: Vec = body .iter() - .filter_map(|kw| if let KW::Noun(K::Name(n)) = kw { Some(n.clone()) } else { None }) + // only collect literally x, y or z. Other names are locals or globals. + .filter_map(|kw| { + if let KW::Noun(K::Name(n)) = kw { + if matches!(n.as_str(), "x" | "y" | "z") { + Some(n.clone()) + } else { + None + } + } else { + None + } + }) .unique() .collect(); args.sort(); @@ -1579,7 +1727,8 @@ pub fn scan_num_token(term: &str) -> Result { match &term[..i] { "0N" => Ok(K::Int(None)), "0n" => Ok(K::Float(f64::NAN)), - "0w" => todo!("inf and ninf https://github.com/kparc/kcc#nulls-and-infinities"), + "0w" => Ok(K::Float(f64::INFINITY)), + "-0w" => Ok(K::Float(f64::NEG_INFINITY)), _ => { if let Ok(i) = term[..i].parse::() { match i { @@ -1612,6 +1761,17 @@ pub fn promote_num(nums: Vec) -> Result { .collect(); Ok(K::FloatArray(Series::new("", fa))) + } else if nums.iter().all(|k| matches!(k, K::Int(Some(1)) | K::Int(Some(0)))) { + let ba: Vec = nums + .iter() + .map(|k| match k { + K::Int(Some(1)) => 1u8, + K::Int(Some(0)) => 0u8, + _ => panic!("invalid bool"), + }) + .collect(); + + Ok(K::BoolArray(Series::new("", ba))) } else if nums.iter().any(|k| matches!(k, K::Int(_))) { let ia: Vec> = nums .iter() @@ -1624,11 +1784,18 @@ pub fn promote_num(nums: Vec) -> Result { Ok(K::IntArray(Series::new("", ia))) } else if nums.iter().all(|k| matches!(k, K::Bool(_))) { - let ba: BooleanChunked = nums + // let ba: BooleanChunked = nums + // .iter() + // .map(|k| match k { + // K::Bool(0) => false, + // K::Bool(1) => true, + // _ => panic!("invalid bool"), + // }) + // .collect(); + let ba: Vec = nums .iter() .map(|k| match k { - K::Bool(0) => false, - K::Bool(1) => true, + K::Bool(i) => *i, _ => panic!("invalid bool"), }) .collect(); diff --git a/src/verbs.rs b/src/verbs.rs index f605321..afcde8b 100644 --- a/src/verbs.rs +++ b/src/verbs.rs @@ -1,10 +1,71 @@ use crate::*; use rand::distributions::{Distribution, Uniform}; use std::cmp; +use std::collections::BTreeMap; use std::iter::repeat; -pub fn v_imat(_x: K) -> Result { Err("nyi") } -pub fn v_group(_x: K) -> Result { Err("nyi") } +pub fn v_imat(x: K) -> Result { + match x { + K::Int(Some(i)) => { + let s = format!("{{a=/:a:!x}}{}", i); + let mut env = Env { names: HashMap::new(), parent: None }; + Ok(eval(&mut env, scan(&s).unwrap()).unwrap().unwrap_noun()) + } + _ => Err("type"), + } +} + +pub fn v_group(x: K) -> Result { + // https://docs.rs/polars/latest/polars/frame/group_by/struct.GroupBy.html#method.get_groups + // https://docs.rs/polars/latest/polars/frame/group_by/struct.GroupBy.html#method.groups + match x { + K::BoolArray(x) | K::IntArray(x) | K::FloatArray(x) => { + //TODO dicts should be IndexMap + let df = DataFrame::new(vec![x.clone().cast(&DataType::String).unwrap()]).unwrap(); + let g = df.group_by_stable([x.name()]).unwrap().groups().unwrap(); + // let keys: K = K::try_from(g.iter().nth(0).unwrap().clone()).unwrap(); + let keys: K = K::SymbolArray( + g.iter() + .nth(0) + .unwrap() + .clone() + .cast(&DataType::Categorical(None, CategoricalOrdering::Lexical)) + .unwrap(), + ); + let vals: Vec = + g.iter().nth(1).unwrap().iter().map(|cell| K::try_from(cell).unwrap()).collect(); + v_makedict(keys, K::List(vals)) + } + K::CharArray(s) => { + let mut letters = IndexMap::new(); + for (i, ch) in s.chars().enumerate() { + (*letters.entry(ch).or_insert(vec![])).extend(vec![K::Int(Some(i as i64))]); + } + let mut d = IndexMap::new(); + for (k, v) in letters { + d.insert(k.to_string(), promote_num(v).unwrap()); + } + Ok(K::Dictionary(d)) + } + K::SymbolArray(_s) => todo!("v_group(SymbolArray(_))"), + K::List(v) => { + let mut im = IndexMap::new(); + for (i, k) in v.iter().enumerate() { + (*im.entry(k).or_insert(vec![])).extend(vec![K::Int(Some(i as i64))]); + } + + let mut d = IndexMap::new(); + for (k, v) in im { + d.insert(k.to_string(), promote_num(v).unwrap()); + } + Ok(K::Dictionary(d)) + } + K::Table(_df) => todo!("v_group(Table(_))"), + + _ => Err("type"), + } +} + pub fn v_equal(x: K, y: K) -> Result { len_ok(&x, &y).and_then(|_| match promote_nouns(x, y) { (K::Bool(l), K::Bool(r)) => Ok(K::Bool((l == r) as u8)), @@ -14,6 +75,9 @@ pub fn v_equal(x: K, y: K) -> Result { (K::BoolArray(l), K::BoolArray(r)) => Ok(K::BoolArray(l.equal(&r).unwrap().into())), (K::IntArray(l), K::IntArray(r)) => Ok(K::BoolArray(l.equal(&r).unwrap().into())), (K::FloatArray(l), K::FloatArray(r)) => Ok(K::BoolArray(l.equal(&r).unwrap().into())), + (K::CharArray(l), K::CharArray(r)) => { + Ok(K::BoolArray(arr!(l.chars().zip(r.chars()).map(|(l, r)| l == r).collect::>()))) + } (K::List(l), K::List(r)) => Ok(K::BoolArray(arr!(zip(l.iter(), r.iter()) .map(|(l, r)| { let (l, r) = promote_nouns(l.clone(), r.clone()); @@ -37,7 +101,30 @@ pub fn v_equal(x: K, y: K) -> Result { } pub fn v_count(x: K) -> Result { Ok(K::Int(Some(x.len().try_into().unwrap()))) } -pub fn v_take(_l: K, _r: K) -> Result { Err("nyi") } +pub fn v_take(x: K, y: K) -> Result { + match x { + K::Bool(0) | K::Int(Some(0)) => match y { + K::Bool(_) | K::BoolArray(_) => Ok(K::BoolArray(Series::new_empty("", &DataType::Boolean))), + K::Int(_) | K::IntArray(_) => Ok(K::IntArray(Series::new_empty("", &DataType::Int64))), + K::Float(_) | K::FloatArray(_) => { + Ok(K::FloatArray(Series::new_empty("", &DataType::Float64))) + } + K::Char(_) | K::CharArray(_) => Ok(K::CharArray("".to_string())), + K::Symbol(_) | K::SymbolArray(_) => Ok(K::SymbolArray(Series::new_empty( + "", + &DataType::Categorical(None, CategoricalOrdering::Lexical), + ))), + K::List(_) => Ok(K::List(vec![])), + K::Dictionary(_) => Ok(K::Dictionary(IndexMap::default())), + K::Table(_) => Ok(K::Table(DataFrame::empty())), + K::Nil => Ok(K::Nil), + K::Name(_) => panic!("impossible"), + }, + K::Bool(1) | K::Int(Some(_)) => v_at(y, v_iota(x).unwrap()), + K::Int(None) => Ok(y), + _ => Err("type"), + } +} #[macro_export] macro_rules! reshape_atom_by_type { @@ -82,6 +169,8 @@ pub fn v_reshape(l: K, r: K) -> Result { } } K::BoolArray(_) => Err("nyi"), + // K::Int(Some(i)) => v_reshape(K::IntArray(arr!([i])), r), + // K::Bool(b) => v_reshape(K::IntArray(arr!([b])), r), _ => Err("type"), } } @@ -90,13 +179,23 @@ pub fn v_ident(x: K) -> Result { Ok(x) } pub fn v_rident(_l: K, r: K) -> Result { Ok(r) } pub fn v_flip(x: K) -> Result { match x { - K::Dictionary(d) => { - if d.iter().map(|(_k, v)| v.len()).all_equal() { + K::Dictionary(ref d) => { + let lengths: Vec = d.iter().map(|(_k, v)| v.len()).unique().sorted().collect(); + if lengths.len() > 2 || lengths.len() == 0 { + Err("length") + } else { + // if d.iter().map(|(_k, v)| v.len()).all_equal() { + let len = lengths.last().unwrap(); let cols: Vec = d .iter() .map(|(k, v)| match v { K::SymbolArray(s) | K::BoolArray(s) | K::IntArray(s) | K::FloatArray(s) => { - Series::new(&k.to_string(), s.clone()) + match s.len() { + 1 => { + Series::new(&k.to_string(), s.extend_constant(s.get(0).unwrap(), *len).unwrap()) + } + _ => Series::new(&k.to_string(), s.clone()), + } } // | K::CharArray(s) => Series::new(&k.to_string(), s.clone()), K::CharArray(s) => Series::new(&k.to_string(), s), @@ -113,12 +212,30 @@ pub fn v_flip(x: K) -> Result { panic!("type error?") } } - _ => todo!("handle atoms"), + K::Bool(b) => { + Series::new(&k.to_string(), std::iter::repeat(*b).take(*len).collect::>()) + } + K::Int(Some(i)) => { + Series::new(&k.to_string(), std::iter::repeat(*i).take(*len).collect::>()) + } + K::Int(None) => Series::full_null(&k.to_string(), *len, &DataType::Int64), + K::Float(f) => { + Series::new(&k.to_string(), std::iter::repeat(*f).take(*len).collect::>()) + } + K::Symbol(s) => Series::new( + &k.to_string(), + std::iter::repeat(s.clone()).take(*len).collect::>(), + ), + // K::Char(c) => Series::new(&k.to_string(), std::iter::repeat(*c.to_string()).take(*len).collect::>()), + K::Char(_c) => todo!("handle char"), + K::Table(_df) => todo!("why is Table here?"), + _ => { + println!("v_flip(x): x: {}", x); + panic!("impossible") + } }) .collect(); Ok(K::Table(DataFrame::new(cols).unwrap())) - } else { - Err("length") } } K::Table(df) => { @@ -129,12 +246,13 @@ pub fn v_flip(x: K) -> Result { .get_columns() .iter() .map(|s| match s.dtype() { - DataType::Boolean => K::BoolArray(s.clone()), + // DataType::Boolean => K::BoolArray(s.clone()), // TODO: cleanup all BoolArray/Boolean usage + DataType::UInt8 => K::BoolArray(s.clone()), DataType::Int64 => K::IntArray(s.clone()), DataType::Float64 => K::FloatArray(s.clone()), // DataType::String => K::CharArray(s.clone()), // TODO K::List([K::CharArray(), ...]) DataType::Categorical { .. } => K::SymbolArray(s.clone()), - _ => panic!("impossible"), + _ => panic!("impossible s.dtype(): {}", s.dtype()), }) .collect(); @@ -206,7 +324,22 @@ macro_rules! atomicdyad { pub fn v_plus(l: K, r: K) -> Result { atomicdyad!(+, v_plus, add, l, r) } pub fn v_negate(x: K) -> Result { Ok(K::Int(Some(-1i64)) * x) } pub fn v_minus(l: K, r: K) -> Result { atomicdyad!(-, v_minus, sub, l, r) } -pub fn v_first(_x: K) -> Result { todo!("implement first") } + +pub fn v_first(x: K) -> Result { + match x { + K::Bool(_) => Ok(x), + K::Int(_) => Ok(x), + K::Float(_) => Ok(x), + K::Char(_) => Ok(x), + K::BoolArray(a) => Ok(K::Bool(a.bool().unwrap().get(0).unwrap() as u8)), + K::IntArray(a) => Ok(K::Int(Some(a.i64().unwrap().get(0).unwrap()))), + K::FloatArray(a) => Ok(K::Float(a.f64().unwrap().get(0).unwrap())), + K::CharArray(a) => Ok(K::Char(a.chars().nth(0).unwrap_or(' '))), + K::List(l) => Ok(l.first().unwrap().clone()), + _ => Err("nyi"), + } +} + pub fn v_times(l: K, r: K) -> Result { match (l.clone(), r.clone()) { // TODO can we make this less repetitive and explicit? @@ -217,8 +350,26 @@ pub fn v_times(l: K, r: K) -> Result { } } pub fn v_sqrt(_x: K) -> Result { todo!("implement sqrt") } -pub fn v_divide(l: K, r: K) -> Result { atomicdyad!(/, v_divide, div, l, r) } -pub fn v_odometer(_r: K) -> Result { todo!("implement odometer") } +pub fn v_divide(l: K, r: K) -> Result { + match (&l, &r) { + (K::Bool(_), K::Bool(0)) => Ok(K::Float(f64::NAN)), + (K::Bool(_), K::Int(Some(0))) => Ok(K::Float(f64::NAN)), + //TODO arrays + _ => atomicdyad!(/, v_divide, div, l, r), + } +} +pub fn v_keys_odometer(x: K) -> Result { + match x { + K::Dictionary(_) => todo!("implement keys"), + K::Table(df) => Ok(K::SymbolArray( + Series::new("", df.fields().iter().cloned().map(|f| f.name.into()).collect::>()) + .cast(&DataType::Categorical(None, CategoricalOrdering::Lexical)) + .unwrap(), + )), + K::IntArray(_) => todo!("implement odometer"), + _ => Err("nyi"), + } +} pub fn v_mod(l: K, r: K) -> Result { match (l, r) { (K::Int(Some(i)), K::IntArray(a)) => Ok(K::IntArray(a % i)), @@ -228,9 +379,45 @@ pub fn v_mod(l: K, r: K) -> Result { } } -pub fn v_where(_r: K) -> Result { Err("nyi") } +pub fn v_where(x: K) -> Result { + match x { + K::BoolArray(b) => { + let indices: Vec = b + .bool() + .unwrap() + .iter() + .enumerate() + .filter(|&(_, value)| value.unwrap()) + .map(|(index, _)| index as i64) + .collect(); + Ok(K::IntArray(arr!(indices))) + } + _ => Err("nyi"), + } +} -pub fn v_reverse(_r: K) -> Result { Err("nyi") } +pub fn v_reverse(x: K) -> Result { + match x { + K::Bool(_) => Ok(x), + K::Int(_) => Ok(x), + K::Float(_) => Ok(x), + K::Char(_) => Ok(x), + K::Symbol(_) => Ok(x), + K::SymbolArray(v) => Ok(K::SymbolArray(v.reverse())), + K::BoolArray(v) => Ok(K::BoolArray(v.reverse())), + K::IntArray(v) => Ok(K::IntArray(v.reverse())), + K::FloatArray(v) => Ok(K::FloatArray(v.reverse())), + K::CharArray(s) => Ok(K::CharArray(s.chars().rev().collect::())), + K::List(v) => Ok(K::List(v.iter().cloned().rev().collect())), + K::Dictionary(m) => { + let mut d = m.clone(); + d.reverse(); + Ok(K::Dictionary(d)) + } + K::Table(df) => Ok(K::Table(df.reverse())), + _ => Err("nyi"), + } +} pub fn v_min(l: K, r: K) -> Result { //TODO fix code duplication in v_min/v_max @@ -298,31 +485,353 @@ pub fn v_max(l: K, r: K) -> Result { }) } -pub fn v_asc(_r: K) -> Result { Err("nyi") } -pub fn v_lesser(_l: K, _r: K) -> Result { Err("nyi") } +pub fn v_asc(x: K) -> Result { + match x { + K::BoolArray(x) => { + let mut map: BTreeMap, Vec> = BTreeMap::new(); + for (i, v) in x.bool().unwrap().iter().enumerate() { + if map.contains_key(&v) { + let vec = map.get(&v).unwrap(); + map.insert(v, vec.iter().chain([i].iter()).cloned().collect()); + } else { + map.insert(v, vec![i]); + } + } + let v: Vec = map.values().flat_map(|v| v.iter().map(|v| *v as i64)).collect(); + Ok(K::BoolArray(arr!(v))) + } + K::IntArray(x) => { + let mut map: BTreeMap, Vec> = BTreeMap::new(); + for (i, v) in x.i64().unwrap().iter().enumerate() { + if map.contains_key(&v) { + let vec = map.get(&v).unwrap(); + map.insert(v, vec.iter().chain([i].iter()).cloned().collect()); + } else { + map.insert(v, vec![i]); + } + } + let v: Vec = map.values().flat_map(|v| v.iter().map(|v| *v as i64)).collect(); + Ok(K::IntArray(arr!(v))) + } + K::FloatArray(x) => { + // f64 is only PartialOrd but we need something with Ord here. + // This is a terrible hack and probably terrible for performance. + let scaled_ints: Vec> = + (x * 1e9).f64().unwrap().into_iter().map(|f| f.map(|f| f as i128)).collect(); + let mut map: BTreeMap, Vec> = BTreeMap::new(); + for (i, v) in scaled_ints.iter().enumerate() { + if map.contains_key(v) { + let vec = map.get(v).unwrap(); + map.insert(*v, vec.iter().chain([i].iter()).cloned().collect()); + } else { + map.insert(*v, vec![i]); + } + } + let v: Vec = map.values().flat_map(|v| v.iter().map(|v| *v as i64)).collect(); + Ok(K::IntArray(arr!(v))) + } + _ => Err("nyi"), + } +} + +pub fn v_desc(x: K) -> Result { + v_reverse(v_asc(x).unwrap()) /* TODO: faster */ +} + +pub fn v_lesser(x: K, y: K) -> Result { + len_ok(&x, &y).and_then(|_| match promote_nouns(x, y) { + (K::Bool(l), K::Bool(r)) => Ok(K::Bool((l < r) as u8)), + (K::Int(Some(l)), K::Int(Some(r))) => Ok(K::Bool((l < r) as u8)), + (K::Int(None), K::Int(Some(_))) => Ok(K::Bool(1)), + (K::Int(Some(_)), K::Int(None)) => Ok(K::Bool(0)), + (K::Int(None), K::Int(None)) => Ok(K::Bool(0)), + (K::Float(l), K::Float(r)) => Ok(K::Bool(match (l.is_nan(), r.is_nan()) { + (false, false) => (l < r) as u8, + (true, false) => 1u8, + (false, true) => 0u8, + (true, true) => 0u8, + })), + (K::BoolArray(l), K::BoolArray(r)) => Ok(K::BoolArray(arr!(zip(l.iter(), r.iter()) + .map(|(l, r)| match (l.is_null(), r.is_null()) { + (false, false) => (l < r) as u8, + (true, false) => 1u8, + (false, true) => 0u8, + (true, true) => 0u8, + }) + .collect::>()))), + (K::IntArray(l), K::IntArray(r)) => Ok(K::BoolArray(arr!(zip(l.iter(), r.iter()) + .map(|(l, r)| match (l.is_null(), r.is_null()) { + (false, false) => (l < r) as u8, + (true, false) => 1u8, + (false, true) => 0u8, + (true, true) => 0u8, + }) + .collect::>()))), + (K::FloatArray(l), K::FloatArray(r)) => Ok(K::BoolArray(arr!(zip(l.iter(), r.iter()) + .map(|(l, r)| { + match (l, r) { + (AnyValue::Float64(l), AnyValue::Float64(r)) => match (l.is_nan(), r.is_nan()) { + (false, false) => (l < r) as u8, + (true, false) => 1u8, + (false, true) => 0u8, + (true, true) => 0u8, + }, + _ => panic!("impossible"), + } + }) + .collect::>()))), + (K::CharArray(l), K::CharArray(r)) => Ok(K::BoolArray(arr!(l + .chars() + .zip(r.chars()) + .map(|(l, r)| (l < r) as u8) + .collect::>()))), + (K::List(l), K::List(r)) => Ok(K::List( + zip(l.iter(), r.iter()) + .map(|(l, r)| v_lesser(l.clone(), r.clone()).unwrap_or(K::Bool(0 as u8))) + .collect::>(), + )), + (K::Dictionary(l), K::Dictionary(r)) => { + Ok(K::Dictionary(IndexMap::from_iter(l.keys().chain(r.keys()).filter_map(|k| { + if l.keys().contains(k) && r.keys().contains(k) { + match v_lesser(l.get(k).unwrap().clone(), r.get(k).unwrap().clone()) { + Ok(r) => Some((k.clone(), r)), + _ => None, + } + } else if l.keys().contains(k) { + // If key is missing on the right, then it's treated as NULL: 1 < 0N == 0 + Some((k.clone(), v_lesser(r.get(k).unwrap().clone(), K::Int(None)).unwrap())) + } else if r.keys().contains(k) { + // If key is missing on the left, then it's treated as NULL: 0N < 1 == 1 + Some((k.clone(), v_lesser(K::Int(None), r.get(k).unwrap().clone()).unwrap())) + } else { + panic!("impossible") + } + })))) + } + (l, K::Dictionary(r)) => Ok(K::Dictionary(IndexMap::from_iter(r.keys().filter_map(|k| { + match v_lesser(l.clone(), r.get(k).unwrap().clone()) { + Ok(r) => Some((k.clone(), r)), + _ => None, + } + })))), + (K::Dictionary(l), r) => Ok(K::Dictionary(IndexMap::from_iter(l.keys().filter_map(|k| { + match v_lesser(l.get(k).unwrap().clone(), r.clone()) { + Ok(l) => Some((k.clone(), l)), + _ => None, + } + })))), + (K::Table(_l), K::Table(_r)) => { + todo!("table") + } + (l, ref r @ K::Table(_)) => { + // TODO: faster. hack: flip to dict, v_lesser(l, r), flip back to table + v_flip(v_lesser(l, v_flip(r.clone()).unwrap()).unwrap()) + } + (l @ K::Table(_), r) => { + // TODO: faster. hack: flip to dict, v_lesser(l, r), flip back to table + v_flip(v_lesser(v_flip(l.clone()).unwrap(), r).unwrap()) + } + _ => Err("nyi"), + }) +} -pub fn v_desc(_r: K) -> Result { Err("nyi") } -pub fn v_greater(_l: K, _r: K) -> Result { Err("nyi") } +pub fn v_greater(x: K, y: K) -> Result { + len_ok(&x, &y).and_then(|_| match promote_nouns(x, y) { + (K::Bool(l), K::Bool(r)) => Ok(K::Bool((l > r) as u8)), + (K::Int(Some(l)), K::Int(Some(r))) => Ok(K::Bool((l > r) as u8)), + (K::Int(None), K::Int(Some(_))) => Ok(K::Bool(0)), + (K::Int(Some(_)), K::Int(None)) => Ok(K::Bool(1)), + (K::Int(None), K::Int(None)) => Ok(K::Bool(0)), + (K::Float(l), K::Float(r)) => Ok(K::Bool(match (l.is_nan(), r.is_nan()) { + (false, false) => (l > r) as u8, + (false, true) => 1u8, + _ => 0u8, + })), + (K::BoolArray(l), K::BoolArray(r)) => Ok(K::BoolArray(arr!(zip(l.iter(), r.iter()) + .map(|(l, r)| match (l.is_null(), r.is_null()) { + (false, false) => (l > r) as u8, + (false, true) => 1u8, + _ => 0u8, + }) + .collect::>()))), + (K::IntArray(l), K::IntArray(r)) => Ok(K::BoolArray(arr!(zip(l.iter(), r.iter()) + .map(|(l, r)| match (l.is_null(), r.is_null()) { + (false, false) => (l > r) as u8, + (false, true) => 1u8, + _ => 0u8, + }) + .collect::>()))), + (K::FloatArray(l), K::FloatArray(r)) => Ok(K::BoolArray(arr!(zip(l.iter(), r.iter()) + .map(|(l, r)| { + match (l, r) { + (AnyValue::Float64(l), AnyValue::Float64(r)) => match (l.is_nan(), r.is_nan()) { + (false, false) => (l > r) as u8, + (false, true) => 1u8, + _ => 0u8, + }, + _ => panic!("impossible"), + } + }) + .collect::>()))), + (K::CharArray(l), K::CharArray(r)) => Ok(K::BoolArray(arr!(l + .chars() + .zip(r.chars()) + .map(|(l, r)| (l > r) as u8) + .collect::>()))), + (K::List(l), K::List(r)) => Ok(K::List( + zip(l.iter(), r.iter()) + .map(|(l, r)| v_lesser(l.clone(), r.clone()).unwrap_or(K::Bool(0 as u8))) + .collect::>(), + )), + (K::Dictionary(l), K::Dictionary(r)) => { + Ok(K::Dictionary(IndexMap::from_iter(l.keys().chain(r.keys()).filter_map(|k| { + if l.keys().contains(k) && r.keys().contains(k) { + match v_lesser(l.get(k).unwrap().clone(), r.get(k).unwrap().clone()) { + Ok(r) => Some((k.clone(), r)), + _ => None, + } + } else if l.keys().contains(k) { + // If key is missing on the right, then it's treated as NULL: 1 > 0N == 0 + Some((k.clone(), v_lesser(r.get(k).unwrap().clone(), K::Int(None)).unwrap())) + } else if r.keys().contains(k) { + // If key is missing on the left, then it's treated as NULL: 0N > 1 == 1 + Some((k.clone(), v_lesser(K::Int(None), r.get(k).unwrap().clone()).unwrap())) + } else { + panic!("impossible") + } + })))) + } + (l, K::Dictionary(r)) => Ok(K::Dictionary(IndexMap::from_iter(r.keys().filter_map(|k| { + match v_lesser(l.clone(), r.get(k).unwrap().clone()) { + Ok(r) => Some((k.clone(), r)), + _ => None, + } + })))), + (K::Dictionary(l), r) => Ok(K::Dictionary(IndexMap::from_iter(l.keys().filter_map(|k| { + match v_lesser(l.get(k).unwrap().clone(), r.clone()) { + Ok(l) => Some((k.clone(), l)), + _ => None, + } + })))), + (K::Table(_l), K::Table(_r)) => { + todo!("table") + } + (l, ref r @ K::Table(_)) => { + // TODO: faster. hack: flip to dict, v_lesser(l, r), flip back to table + v_flip(v_lesser(l, v_flip(r.clone()).unwrap()).unwrap()) + } + (l @ K::Table(_), r) => { + // TODO: faster. hack: flip to dict, v_lesser(l, r), flip back to table + v_flip(v_lesser(v_flip(l.clone()).unwrap(), r).unwrap()) + } + _ => Err("nyi"), + }) +} pub fn v_not(_r: K) -> Result { Err("nyi") } pub fn v_match(_l: K, _r: K) -> Result { Err("nyi") } -pub fn v_enlist(_r: K) -> Result { Err("nyi") } -pub fn v_concat(_l: K, _r: K) -> Result { Err("nyi") } +pub fn v_enlist(x: K) -> Result { + match x { + K::Bool(x) => Ok(K::BoolArray(arr!([x]))), + K::Int(x) => Ok(K::IntArray(arr!([x]))), + K::Float(x) => Ok(K::FloatArray(arr!([x]))), + K::Char(x) => Ok(K::CharArray(x.to_string())), + K::Symbol(x) => Ok(K::SymbolArray( + Series::new("", [x.to_string()]) + .cast(&DataType::Categorical(None, CategoricalOrdering::Lexical)) + .unwrap(), + )), + K::BoolArray(_) + | K::IntArray(_) + | K::FloatArray(_) + | K::CharArray(_) + | K::List(_) + | K::Dictionary(_) + | K::Table(_) => Ok(K::List(vec![x])), + _ => Err("nyi v_enlist() other cases"), + } +} + +pub fn v_concat(x: K, y: K) -> Result { + match (x.clone(), y.clone()) { + (K::Bool(_) | K::Int(_) | K::Float(_), K::Bool(_) | K::Int(_) | K::Float(_)) => { + promote_num(vec![x, y]) + } + (K::BoolArray(x) | K::IntArray(x) | K::FloatArray(x), K::FloatArray(y)) => { + Ok(K::FloatArray(x.to_float().unwrap().extend(&y).unwrap().clone())) + } + (K::FloatArray(mut x), K::BoolArray(y) | K::IntArray(y)) => { + Ok(K::FloatArray(x.extend(&y.to_float().unwrap()).unwrap().clone())) + } + (K::BoolArray(x) | K::IntArray(x), K::IntArray(y)) => { + Ok(K::IntArray(x.cast(&DataType::Int64).unwrap().extend(&y).unwrap().clone())) + } + (K::BoolArray(mut x), K::BoolArray(y)) => { + Ok(K::IntArray(x.extend(&y.cast(&DataType::Boolean).unwrap()).unwrap().clone())) + } + (K::Bool(_) | K::Int(_) | K::Float(_), K::BoolArray(_) | K::IntArray(_) | K::FloatArray(_)) => { + v_concat(v_enlist(x).unwrap(), y) + } + (K::BoolArray(_) | K::IntArray(_) | K::FloatArray(_), K::Bool(_) | K::Int(_) | K::Float(_)) => { + v_concat(x, v_enlist(y).unwrap()) + } + (K::Char(x), K::Char(y)) => Ok(K::CharArray(format!("{}{}", x, y))), + (K::CharArray(x), K::Char(y)) => Ok(K::CharArray(format!("{}{}", x, y))), + (K::Char(x), K::CharArray(y)) => Ok(K::CharArray(format!("{}{}", x, y))), + + (K::List(x), K::List(y)) => Ok(K::List(x.iter().chain(y.iter()).cloned().collect())), + (K::List(x), y) => Ok(K::List(x.iter().chain(vec![y].iter()).cloned().collect())), + (x, K::List(y)) => Ok(K::List(vec![x].iter().chain(y.iter()).cloned().collect())), + _ => todo!("nyi v_concat() other cases {}, {}", x, y), + } +} -pub fn v_isnull(_r: K) -> Result { Err("nyi") } +pub fn v_isnull(x: K) -> Result { + match x { + K::Int(None) => Ok(K::Bool(1u8)), + K::Float(f) if f.is_nan() => Ok(K::Bool(1u8)), + _ => Ok(K::Bool(0u8)), + } +} pub fn v_fill(_l: K, _r: K) -> Result { Err("nyi") } pub fn v_except(_l: K, _r: K) -> Result { Err("nyi") } pub fn v_floor(_r: K) -> Result { Err("nyi") } -pub fn v_drop(_l: K, _r: K) -> Result { Err("nyi") } +pub fn v_drop(x: K, y: K) -> Result { + match x { + K::Bool(1) => v_drop(K::Int(Some(1)), y), + K::Int(Some(0)) | K::Bool(0) => Ok(y), + K::Int(Some(i)) => { + if i.abs() as usize >= y.len() { + v_take(K::Int(Some(0)), y) + } else if i < 0 { + // drop off end. + // TODO efficient K slicing + v_at(y.clone(), K::IntArray(arr!((0..(y.len() as i64 - i.abs())).collect::>()))) + } else { + // drop off front. + // TODO efficient K slicing + v_at(y.clone(), K::IntArray(arr!((i..(y.len() as i64)).collect::>()))) + } + } + _ => Err("type"), + } +} pub fn v_delete(_l: K, _r: K) -> Result { Err("nyi") } pub fn v_cut(_l: K, _r: K) -> Result { Err("nyi") } pub fn v_string(_r: K) -> Result { Err("nyi") } pub fn v_dfmt(_l: K, _r: K) -> Result { Err("nyi") } pub fn v_pad(_l: K, _r: K) -> Result { Err("nyi") } -pub fn v_cast(_l: K, _r: K) -> Result { Err("nyi") } +pub fn v_cast(l: K, _r: K) -> Result { + match l { + K::Symbol(s) if s == "c".to_string() => todo!("cast to string"), + K::Symbol(s) if s == "i".to_string() => todo!("cast to int"), + K::Symbol(s) if s == "f".to_string() => todo!("cast to float"), + _ => Err("type"), + } +} pub fn v_randfloat(r: K) -> Result { match r { @@ -368,7 +877,31 @@ pub fn v_rand(l: K, r: K) -> Result { _ => Err("nyi"), } } -pub fn v_find(_l: K, _r: K) -> Result { Err("nyi") } +pub fn v_find(x: K, y: K) -> Result { + // find index of every item of y in x + match (x, y) { + (K::BoolArray(_x), K::BoolArray(_y)) => todo!("BoolArray "), + (K::IntArray(_x), K::IntArray(_y)) => todo!("IntArray "), + (K::FloatArray(_x), K::FloatArray(_y)) => todo!("FloatArray "), + (K::CharArray(x), K::CharArray(y)) => { + if let K::CharArray(uniq_y) = v_unique(K::CharArray(y.clone())).unwrap() { + let map: IndexMap> = uniq_y + .chars() + .map(|c| (c, x.chars().position(|cc| cc == c))) + .map(|(c, i)| match i { + Some(i) => (c, Some(i as i64)), + _ => (c, None), + }) + .collect(); + let res: Vec> = y.chars().map(|c| map.get(&c).unwrap()).cloned().collect(); + Ok(K::IntArray(arr!(res))) + } else { + panic!("impossible") + } + } + _ => Err("nyi v_find"), + } +} pub fn v_splice(_x: K, _y: K, _z: K) -> Result { Err("nyi") } pub fn v_type(r: K) -> Result { @@ -436,19 +969,19 @@ pub fn v_at(l: K, r: K) -> Result { let c: char = a.chars().nth(i as usize).unwrap(); Ok(K::Char(c)) } - K::List(_v) => todo!("v_at List"), + K::List(v) => Ok(v.get(i as usize).unwrap().clone()), _ => todo!("index into l"), } } else { l.fill(0) } } - K::BoolArray(i) => v_at(l, K::try_from(i.cast(&DataType::Int64).unwrap()).unwrap()), - K::IntArray(i) => { + K::BoolArray(i) => v_at(l, K::IntArray(i.cast(&DataType::Int64).unwrap())), + K::IntArray(ref i) => { // https://docs.rs/polars/latest/polars/series/struct.Series.html#method.take_threaded // Notes: Out of bounds access doesn’t Error but will return a Null value // TODO Add fills not Nulls - let i = Series::new( + let i_u32 = Series::new( "", i.i64() .unwrap() @@ -456,32 +989,62 @@ pub fn v_at(l: K, r: K) -> Result { .map(|i| i.unwrap_or(4_294_967_295) as u32) .collect::>(), ); - let idcs: Vec = i.u32().unwrap().into_iter().map(|i| i.unwrap()).collect::>(); + let r_len = r.clone().len(); + let idcs: Vec = i_u32 + .u32() + .unwrap() + .into_iter() + .map(|i| match i { + Some(i) => i.min(r_len.try_into().unwrap_or(4_294_967_295)), + _ => r_len.try_into().unwrap_or(4_294_967_295), + }) + .collect::>(); match l.clone() { - K::SymbolArray(a) => match a.take_slice(&idcs) { - Ok(a) => Ok(K::SymbolArray(a)), - _ => todo!("index out of bounds - this shouldn't be an error"), - }, - K::BoolArray(a) => match a.take_slice(&idcs) { - Ok(a) => Ok(K::BoolArray(a)), - _ => todo!("index out of bounds - this shouldn't be an error"), - }, - K::IntArray(a) => match a.take_slice(&idcs) { - Ok(a) => Ok(K::IntArray(a)), - _ => todo!("index out of bounds - this shouldn't be an error"), - }, - K::FloatArray(a) => match a.take_slice(&idcs) { - Ok(a) => Ok(K::FloatArray(a)), - _ => todo!("index out of bounds - this shouldn't be an error"), - }, + K::SymbolArray(a) => { + match a.clone().append(&Series::new_null("", 1)).unwrap().take_slice(&idcs) { + Ok(a) => Ok(K::SymbolArray(a)), + _ => todo!("index out of bounds - this shouldn't be an error"), + } + } + K::BoolArray(a) => { + match a.clone().append(&Series::new_null("", 1)).unwrap().take_slice(&idcs) { + Ok(a) => Ok(K::BoolArray(a)), + _ => todo!("index out of bounds - this shouldn't be an error"), + } + } + K::IntArray(a) => { + match a.clone().append(&Series::new_null("", 1)).unwrap().take_slice(&idcs) { + Ok(a) => Ok(K::IntArray(a)), + _ => todo!("index out of bounds - this shouldn't be an error"), + } + } + K::FloatArray(a) => { + match a.clone().append(&Series::new_null("", 1)).unwrap().take_slice(&idcs) { + Ok(a) => Ok(K::FloatArray(a)), + _ => todo!("index out of bounds - this shouldn't be an error"), + } + } K::CharArray(a) => Ok(K::CharArray( - i.u32() + i_u32 + .u32() .unwrap() .iter() .map(|i| a.chars().nth(i.unwrap() as usize).unwrap_or(' ')) .collect(), )), - K::List(_) => todo!("v_at K::List"), + K::List(v) => { + let r: Vec = i + .i64() + .unwrap() + .into_iter() + .map(|i| match i { + None => K::Int(None), + Some(i) if i < 0 || i as usize >= v.len() => K::Int(None), + _ => v.get(i.unwrap() as usize).unwrap().clone(), + }) + .collect(); + Ok(promote_num(r.clone()).unwrap_or(K::List(r))) + } _ => todo!("v_at"), } } @@ -536,7 +1099,16 @@ pub fn v_at(l: K, r: K) -> Result { pub fn v_amend3(_x: K, _y: K, _z: K) -> Result { Err("nyi") } pub fn v_amend4(_x: K, _y: K, _f: K, _z: K) -> Result { Err("nyi") } -pub fn v_eval(_r: K) -> Result { Err("nyi") } +pub fn v_eval(x: K) -> Result { + // TODO: does this need the current Env passed in? + match x { + K::CharArray(s) => { + let mut env = Env { names: HashMap::new(), parent: None }; + Ok(eval(&mut env, scan(&s).unwrap()).unwrap().unwrap_noun()) + } + _ => Err("nyi"), + } +} pub fn v_dot(_l: K, _r: K) -> Result { Err("nyi") } // https://k.miraheze.org/wiki/Deep_amend pub fn v_deepamend3(_x: K, _y: K, _z: K) -> Result { Err("nyi") } @@ -574,11 +1146,14 @@ pub fn v_split(l: K, r: K) -> Result { } } -pub fn v_iota(r: K) -> Result { +pub fn v_iota(x: K) -> Result { debug!("v_iota"); - match r { + match x { + K::Bool(0) => Ok(K::IntArray(Series::new_empty("", &DataType::Int64))), + K::Bool(1) => v_iota(K::Int(Some(1))), + K::Int(Some(0)) | K::Int(None) => Ok(K::IntArray(Series::new_empty("", &DataType::Int64))), K::Int(Some(i)) => Ok(K::IntArray(arr![(0..i).collect::>()])), - _ => todo!("v_iota variants"), + _ => todo!("v_iota variants: {}", x), } } pub fn v_sum(x: K) -> Result { @@ -610,7 +1185,12 @@ pub fn v_d_sum(l: K, r: K) -> Result { Ok(l + v_sum(r).unwrap() pub fn v_d_bang(l: K, r: K) -> Result { match l { - K::SymbolArray(_) | K::Symbol(_) => v_makedict(l, r), + K::SymbolArray(_) + | K::Symbol(_) + | K::BoolArray(_) + | K::IntArray(_) + | K::FloatArray(_) + | K::CharArray(_) => v_makedict(l, r), _ => v_mod(l, r), } } @@ -782,7 +1362,7 @@ pub fn v_scan(env: &mut Env, v: KW, x: K) -> Result { // same as v_fold() except return a K::List of intermediate results // split into list, then scan match v { - f @ KW::Verb { .. } | f @ KW::Function { .. } => k_to_vec(x.clone()).and_then(|v| { + f @ KW::Verb { .. } | f @ KW::Function { .. } => k_to_vec(x.clone()).map(|v| { let mut result: Vec = vec![v[0].clone()]; for i in v[1..].iter() { result.push( @@ -801,26 +1381,117 @@ pub fn v_scan(env: &mut Env, v: KW, x: K) -> Result { ) } match promote_num(result.clone()) { - Ok(k) => Ok(k), - _ => Ok(K::List(result)), + Ok(k) => k, + _ => K::List(result), } }), _ => Err("type"), } } -pub fn v_d_scan(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { todo!("scan") } +pub fn v_d_scan(env: &mut Env, v: KW, x: K, y: K) -> Result { + match v.clone() { + f @ KW::Verb { .. } | f @ KW::Function { .. } => { + let first: K = eval( + env, + vec![ + f.clone(), + KW::FuncArgs(vec![ + vec![KW::Noun(x.clone())], + vec![KW::Noun(v_first(y.clone()).unwrap())], + ]), + ], + ) + .unwrap() + .unwrap_noun(); + + let rest: K = v_drop(K::Int(Some(1)), y).unwrap(); + v_scan(env, v, v_concat(first, rest).unwrap()) + } + _ => Err("type"), + } +} -pub fn v_eachprior(_env: &mut Env, _v: KW, _x: K) -> Result { - todo!("v_eachprior()") +pub fn v_eachprior(env: &mut Env, v: KW, x: K) -> Result { + match v { + f @ KW::Verb { .. } | f @ KW::Function { .. } => k_to_vec(x).map(|v| { + let first: &K = &v[0]; + let r: Vec = v + .iter() + .zip(v.iter().skip(1)) + .map(|(x, y)| { + // f[y,x] / yes y,x not x,y + eval( + env, + vec![ + f.clone(), + KW::FuncArgs(vec![vec![KW::Noun(y.clone())], vec![KW::Noun(x.clone())]]), + ], + ) + .unwrap() + .unwrap_noun() + }) + .collect(); + let r: Vec = vec![first.clone()].into_iter().chain(r).collect(); + promote_num(r.clone()).unwrap_or(K::List(r)) + }), + _ => Err("type"), + } +} +pub fn v_eachprior_d_or_windows(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { + todo!("v_eachprior_d_or_windows()") +} +pub fn v_eachprior_d(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { + todo!("v_eachprior_d()") } pub fn v_windows(_env: &mut Env, _v: KW, _x: K, _y: K) -> Result { todo!("v_windows()") } -pub fn v_eachright(_env: &mut Env, _v: KW, _x: K) -> Result { - todo!("v_eachright()") + +pub fn v_d_eachright(env: &mut Env, v: KW, x: K, y: K) -> Result { + match v { + f @ KW::Verb { .. } | f @ KW::Function { .. } => k_to_vec(y).map(|v| { + let r: Vec = v + .iter() + .cloned() + .map(|y| { + eval( + env, + vec![ + f.clone(), + KW::FuncArgs(vec![vec![KW::Noun(x.clone())], vec![KW::Noun(y.clone())]]), + ], + ) + .unwrap() + .unwrap_noun() + }) + .collect(); + promote_num(r.clone()).unwrap_or(K::List(r)) + }), + _ => Err("type"), + } } -pub fn v_eachleft(_env: &mut Env, _v: KW, _x: K) -> Result { - todo!("v_eachleft()") +pub fn v_d_eachleft(env: &mut Env, v: KW, x: K, y: K) -> Result { + match v { + f @ KW::Verb { .. } | f @ KW::Function { .. } => k_to_vec(x).map(|v| { + let r: Vec = v + .iter() + .cloned() + .map(|x| { + eval( + env, + vec![ + f.clone(), + KW::FuncArgs(vec![vec![KW::Noun(x.clone())], vec![KW::Noun(y.clone())]]), + ], + ) + .unwrap() + .unwrap_noun() + }) + .collect(); + promote_num(r.clone()).unwrap_or(K::List(r)) + }), + _ => Err("type"), + } } pub fn strip_quotes(s: String) -> String { diff --git a/tests/aoc.rs b/tests/aoc.rs new file mode 100644 index 0000000..f55e61d --- /dev/null +++ b/tests/aoc.rs @@ -0,0 +1,124 @@ +use std::collections::HashMap; + +use roklang::*; + +use roklang::KW::*; + +#[test] +fn test_aoc2015_12_01() { + let mut env = Env { names: HashMap::new(), parent: None }; + + assert_eq!( + eval(&mut env, scan(r#"(+/-1*")"=s)+(+/"("=s:"()")"#).unwrap()).unwrap(), + Noun(K::Int(Some(0))) + ); +} + +#[ignore] +#[test] +fn test_aoc2015_12_01_v2() { + let mut env = Env { names: HashMap::new(), parent: None }; + + // TODO + assert_eq!( + eval(&mut env, scan(r#"+//1 -1*"()"=\:s:"((()))""#).unwrap()).unwrap(), + Noun(K::Int(Some(0))) + ); +} + +#[test] +fn test_aoc2015_12_01_p2() { + let mut env = Env { names: HashMap::new(), parent: None }; + + assert_eq!( + eval(&mut env, scan(r#"1+*&-1 = +\(-1*")"=s)+("("=s:"(()))()")"#).unwrap()).unwrap(), + Noun(K::Int(Some(5))) + ); +} + +#[ignore] +#[test] +fn test_aoc2015_12_01_p2_v2() { + let mut env = Env { names: HashMap::new(), parent: None }; + + // TODO + assert_eq!( + eval(&mut env, scan(r#"1+*&-1=+\+/1 -1*"()"=\:s:"(()))()")"#).unwrap()).unwrap(), + Noun(K::Int(Some(5))) + ); +} + +#[test] +fn test_aoc2015_12_02() { + let mut env = Env { names: HashMap::new(), parent: None }; + + // 20x3x11 + // 15x27x5 + // 6x29x7 + // {"x"\x}'s:("20x3x11";"15x27x5";"6x29x7") + let res = + eval(&mut env, scan(r#".''{"x"\x}'s:("20x3x11";"15x27x5";"6x29x7")"#).unwrap()).unwrap(); + let expected = eval(&mut env, scan(r#"((20 3 11);(15 27 5);(6 29 7))"#).unwrap()).unwrap(); + assert_eq!(res, expected); + + let res = eval( + &mut env, + scan(r#"{((x@0)*(x@1);(x@1)*(x@2);(x@0)*(x@2))}'{.'"x"\x}'s:("20x3x11";"15x27x5";"6x29x7")"#) + .unwrap(), + ) + .unwrap(); + let expected = + eval(&mut env, scan(r#"((60 33 220);(405 135 75);(174 203 42))"#).unwrap()).unwrap(); + assert_eq!(res, expected); + + let s = r#"{l:x@0;w:x@1;h:x@2;d:(l*w;w*h;l*h);+/(2*d),&/d}' {.'"x"\x}'s:("2x3x4";"1x1x10")"#; + let res = eval(&mut env, scan(s).unwrap()).unwrap(); + let expected = eval(&mut env, scan("58 43").unwrap()).unwrap(); + assert_eq!(res, expected); + + // TODO Why is this so slow? + let s = r#"+/{l:x@0;w:x@1;h:x@2;d:(l*w;w*h;l*h);+/(2*d),&/d}' {.'"x"\x}'s:0:"tests/aoc/2015/day02.txt""#; + let res = eval(&mut env, scan(s).unwrap()).unwrap(); + let expected = eval(&mut env, scan("1606483").unwrap()).unwrap(); + assert_eq!(res, expected); +} + +#[ignore] +#[test] +fn test_aoc2015_12_02_v2() { + let mut env = Env { names: HashMap::new(), parent: None }; + + let s = r#"(2*+//s)+/&/s:(*|x)*':x:`I$+"x"\'0:"tests/aoc/2015/day02.txt""#; + let res = eval(&mut env, scan(s).unwrap()).unwrap(); + let expected = eval(&mut env, scan("1606483").unwrap()).unwrap(); + assert_eq!(res, expected); +} + +#[test] +fn test_aoc2015_12_02_p2() { + let mut env = Env { names: HashMap::new(), parent: None }; + + // let s = r#"{(*/x)+/2*2#x["?*i:0:"day3.txt" + let s = r#"#=santa:0 0+\(0 1;0 -1;-1 0;1 0)@"^v<>"?*0:"tests/aoc/2015/day03.txt""#; + let res = eval(&mut env, scan(s).unwrap()).unwrap(); + let expected = eval(&mut env, scan("2572").unwrap()).unwrap(); + assert_eq!(res, expected); + + // part 2 + // let s = r#"#=santa,robot:0 0+\(0 -1;0 1;1 0;-1 0)@"^v<>"?*0:"tests/aoc/2015/day03.txt""#; + // let res = eval(&mut env, scan(s).unwrap()).unwrap(); + // let expected = eval(&mut env, scan("1234").unwrap()).unwrap(); // 4403 too high + // assert_eq!(res, expected); +} diff --git a/tests/aoc/2015/day01.k b/tests/aoc/2015/day01.k new file mode 100644 index 0000000..995b727 --- /dev/null +++ b/tests/aoc/2015/day01.k @@ -0,0 +1 @@ +(+/-1*")"=s)+(+/"("=s:*0:"day01.txt") diff --git a/tests/aoc/2015/day01.p2.k b/tests/aoc/2015/day01.p2.k new file mode 100644 index 0000000..b8e355e --- /dev/null +++ b/tests/aoc/2015/day01.p2.k @@ -0,0 +1 @@ +*&-1=+\+/1 -1*"()"=\:s:*0:"day01.txt" diff --git a/tests/aoc/2015/day01.txt b/tests/aoc/2015/day01.txt new file mode 100644 index 0000000..2d4ad8f --- /dev/null +++ b/tests/aoc/2015/day01.txt @@ -0,0 +1 @@ +()(((()))(()()()((((()(((())(()(()((((((()(()(((())))((()(((()))((())(()((()()()()(((())(((((((())))()()(()(()(())(((((()()()((())(((((()()))))()(())(((())(())((((((())())))(()())))()))))()())()())((()()((()()()()(()((((((((()()())((()()(((((()(((())((())(()))()((((()((((((((())()((()())(())((()))())((((()())(((((((((((()()(((((()(()))())(((()(()))())((()(()())())())(()(((())(())())()()(()(()((()))((()))))((((()(((()))))((((()(()(()())())()(((()((((())((((()(((()()(())()()()())((()((((((()((()()))()((()))()(()()((())))(((()(((()))((()((()(()))(((()()(()(()()()))))()()(((()(((())())))))((()(((())()(()(())((()())))((((())))(()(()(()())()((()())))(((()((()(())()()((()((())(()()((())(())()))()))((()(())()))())(((((((()(()()(()(())())))))))(()((((((())((((())((())())(()()))))()(())(()())()())((())(()))))(()))(()((()))()(()((((((()()()()((((((((()(()(())((()()(()()))(())()())()((())))()))()())(((()))(())()(())()))()((()((()(()()())(())()()()((())())))((()()(()()((()(())()()())(((()(()()))))(())))(()(()())()))()()))))))()))))((((((())))())))(()(())())(()())))))(()))()))))))()((()))))()))))(()(()((()())())(()()))))(((())()))())())())(((()(()()))(())()(())(())((((((()()))))((()(()))))))(()))())(((()()(()))()())()()()())))))))))))))(())(()))(()))((()(())(()())(())())(()())(())()()(()())))()()()))(())())()))())())(())((())))))))(())))(())))))()))))((())(()(((()))))(()))()((()(())))(()())(((((()))()())()()))))()))))()))())(()(()()()))()))))))((()))))))))))()((()))((()(())((())()()(()()))()(()))))()()(()))()))(((())))(())()((())(())(()())()())())))))))())))()((())))()))(()))()()))(((((((()))())(()()))(()()(()))()(()((()())()))))))(((()()()())))(())()))()())(()()))()()))))))))(())))()))()()))))))()))()())))()(())(())))))()(())()()(()()))))())((()))))()))))(()(((((()))))))))())))())()(())()()))))(())))())()()())()()())()(()))))()))()))))))))())))((()))()))()))())))()())()()())))())))(()((())()((()))())))))())()(())((())))))))))))())()())(())())())(()))(()))()))())(()(())())()())()()(()))))(()(())))))))(())))())(())))))))())()()(())())())))(())))))()))()(()())()(()))())())))))()()(()))()))))())))))))))()))))()))))))())()())()()))))()())))())))))))))))()()))))()()(((()))()()(())()))))((()))))(()))(())())))(())()))))))(()))()))))(())())))))()))(()())))))))))))))())))))))))()((()())(()())))))))((()))))(())(())))()(()())())))())())(()()()())))()))))))())))))())()()())))))))))))()()(()))))()())()))((()())(()))))()(()))))))))))()())())(((())(()))))())()))()))()))))))()))))))(()))))()))))()(())))(())))(()))())()()(()()))()))(()()))))))))()))(()))())(()()(()(()())()()))()))))))))(())))))((()()(()))())())))))()))())(()())()()))())))()(()()()()))((())())))())()(()()))()))))))))(()))(())))()))))(()(()())(()))))()())())()))()()))())))))))))))())()))))))()))))))))())))))()))))())(()())))(())()))())())))))()()(()()())(()())))()()))(((()))(()()()))))()))))()))))((())))()((((((()()))))))())))))))))))(((()))))))))))))(())())))))())(()))))))(()))((()))())))()(()((()))()))()))))))))))())()))()(()()))))())))())(())()(()))()))())(()))()))))(()()))()()(())))))()))(())(()(()()))(()()())))))(((()))))))()))))))))))))(())(()))))()())())()()((()()))())))))(()))))())))))))()()()))))))))())))()(((()()))(())))))(((())())))))((()))()(()))(()))))(()())))(()))())))))()))))(())(())))()((()))(())())))()()))()))))))))()))(()()()(()()()(()))())(())()())(((()))(())))))))))(((()())))()()))))))))()(())(()))()((((())(())(()())))()))(((())()()()))((()))(()))())())))())))(()))())()())())(()(())())()()()(())))())(())))(())))(())()))()))(()((()))))))))())(()))))))())(()()))()()))()(()(()())))()()(()((()((((((()))(())))()()()))())()))((()()(()))())((()(()(()))(()()))))()())))()))()())))))))()()((()())(())))()))(()))(())(()))())(()(())))()()))))))(((()(((()()))()(()(())())((()()))()))()))()))()(()()()(()))((()())()(())))()()))(((())()()())(())()((()()()()(()(())(()()))()(((((()())))((())))))(()()()))))(((()(())))()))((()((()(())()(()((())))((()())()(()))(((()())()()(()))(())(((()((()())()((())()())(((()()))((()((())(()))(()())(()()()))((()))(())(()((()()())((()))(())))(())(())(())))(()())))(((((()(()(((((()())((((()(()())(())(()()(((())((()(((()()(((()()((((((())))())(()((((((()(()))()))()()((()((()))))()(()()(()((()()))))))(((((()(((((())()()()(())())))))))()))((()()(())))(())(()()()())))))(()((((())))))))()()(((()(()(()(()(()())()()()(((((((((()()())()(()))((()()()()()(((((((()())()((())()))((((((()(()(()(()())(((()(((((((()(((())(((((((((())(())())()))((()(()))(((()()())(())(()(()()(((()(())()))())))(())((((((())(()()())()()(((()(((())(()(((())(((((((()(((((((((()))(())(()(()(()))))((()))()(())())())((()(()((()()))((()()((()(())(())(()((())(((())(((()()()((((((()()(())((((())()))))(())((()(()((())))(((((()(()()())())((())())))((())((()((()()((((((())(((()()(()())())(()(()))(()(()))())())()(((((((()(((()(())()()((())((()(()()((()(()()(((((((((((())((())((((((())((()((((()(()((((()(((((((())()((()))))())()((()((((()(()(((()((()())))(())())(((()(((())((((((()(((((((((()()(())))(()(((((()((((()())))((()((()((()(()()(((())((((((((((((()(((())(()(((((()))(()()(()()()()()()((())(((((((())(((((())))))())()(()()(()(()(((()()(((((())(()((()((()(((()()((()((((())()))()((((())(())))()())(((())(())(()()((()(((()()((((((((((()()(()())())(((((((((())((((()))()()((((())(()((((()(((())())(((((((((((()((((())))(())(()(((()(((()((())(((((()((()()(()(()()((((((()((((()((()(()((()(()((((((()))))()()(((((()((()(()(())()))(())(((((((()((((()())(()((()((()(()))())))(())((()))))(((((((()()()())(()))(()()((()())()((()((()()()(()(()()))(()())(())(((((()(((((((((((()((()(((()(((((((()()((((((()(((((()(()((()(((((())((((((()))((((())((()()((())(((())()(((((()()(((((()((()(()(((((((()(((((()((()((()((())(())((())(()))()()))(()()(()(()()(((((((()(((()(((())()(((((()((((((()())((((())()((()((()(()()())(()))((((()()((((((()((()(()(()((((()((()((())((((((()(()(())((((((()((((((((((()((())()))()(()(()(((((()()()))((())))()(()((((((((((((((()(((()((((()((())((()((()(((()()(()(((()((())(()()())))()(()(()(((((()()(()(()((((()(((((())()(()(()))(((((()()(((()()(())((((((((((((((())((())(((((((((((())()()()(())()(()(()(((((((((())(((()))(()()())(()((((()(())(((((()())(())((((((((())()((((()((((((())(()((()(())(((()((((()))(((((((((()()))((((()(())()()()(())(()((())((()()))()(((())(((((())((((((()()))(((((((((()((((((())))(((((((()((()(()(())))())(()(()))()(((((()())(()))()(()(())(((()))))())()())))(((((()))())()((()(()))))((()()()((((((()))()()((((((((())((()(()(((()(()((())((()())(()((((())(()(((()()()(()(()()))())())((((((((((())())((()))()((())(())(())))())()(()()(())))())(()))(((()(()()(((()(((())))()(((()(())()((((((())()))()))()((((((()(()(((((()())))()))))())()()(((()(((((())((()()(()((()((()(()(()(())))(()()()()((()(())(((()((()))((((()))())(())))())(()))()()()())()))(((()()())()((())))(())(()()()()(()())((()(()()((((())))((()((()(())((()(()((())()(()()(((()())()()())((()))((())(((()()(())))()()))(((()((())()(((((()())(())((())()())())((((((()(()(((((()))(()( diff --git a/tests/aoc/2015/day02.txt b/tests/aoc/2015/day02.txt new file mode 100644 index 0000000..2f04474 --- /dev/null +++ b/tests/aoc/2015/day02.txt @@ -0,0 +1,1000 @@ +20x3x11 +15x27x5 +6x29x7 +30x15x9 +19x29x21 +10x4x15 +1x26x4 +1x5x18 +10x15x23 +10x14x20 +3x5x18 +29x23x30 +7x4x10 +22x24x29 +30x1x2 +19x2x5 +11x9x22 +23x15x10 +11x11x10 +30x28x5 +22x5x4 +6x26x20 +16x12x30 +10x20x5 +25x14x24 +16x17x22 +11x28x26 +1x11x10 +1x24x15 +13x17x21 +30x3x13 +20x25x17 +22x12x5 +22x20x24 +9x2x14 +6x18x8 +27x28x24 +11x17x1 +1x4x12 +5x20x13 +24x23x23 +22x1x25 +18x19x5 +5x23x13 +8x16x4 +20x21x9 +1x7x11 +8x30x17 +3x30x9 +6x16x18 +22x25x27 +9x20x26 +16x21x23 +5x24x17 +15x17x15 +26x15x10 +22x16x3 +20x24x24 +8x18x10 +23x19x16 +1x21x24 +23x23x9 +14x20x6 +25x5x5 +16x3x1 +29x29x20 +11x4x26 +10x23x24 +29x25x16 +27x27x22 +9x7x22 +6x21x18 +25x11x19 +14x13x3 +15x28x17 +14x3x12 +29x8x19 +30x14x20 +20x23x4 +8x16x5 +4x11x18 +20x8x24 +21x13x21 +14x26x29 +27x4x17 +27x4x25 +5x28x6 +23x24x11 +29x22x5 +30x20x6 +23x2x10 +11x4x7 +27x23x6 +10x20x19 +8x20x22 +5x29x22 +16x13x2 +2x11x14 +6x12x4 +3x13x6 +16x5x18 +25x3x28 +21x1x5 +20x16x19 +28x30x27 +26x7x18 +25x27x24 +11x19x7 +21x19x17 +2x12x27 +20x5x14 +8x5x8 +6x24x8 +7x28x20 +3x20x28 +5x20x30 +13x29x1 +26x29x5 +19x28x25 +5x19x11 +11x20x22 +4x23x1 +19x25x12 +3x10x6 +3x14x10 +28x16x12 +23x12x2 +23x12x19 +20x28x10 +9x10x25 +16x21x16 +1x18x20 +9x4x26 +3x25x8 +17x16x28 +9x28x16 +27x3x12 +17x24x12 +13x21x10 +7x17x13 +6x10x9 +7x29x25 +11x19x30 +1x24x5 +20x16x23 +24x28x21 +6x29x19 +25x2x19 +12x5x26 +25x29x12 +16x28x22 +26x26x15 +9x13x5 +10x29x7 +1x24x16 +22x2x2 +6x16x13 +3x12x28 +4x12x13 +14x27x21 +14x23x26 +7x5x18 +8x30x27 +15x9x18 +26x16x5 +3x29x17 +19x7x18 +16x18x1 +26x15x30 +24x30x21 +13x20x7 +4x12x10 +27x20x11 +28x29x21 +20x14x30 +28x12x3 +19x1x8 +4x8x6 +21x14x2 +27x19x21 +17x24x14 +15x18x11 +18x7x26 +25x28x29 +27x26x9 +18x12x17 +24x28x25 +13x24x14 +26x9x28 +9x3x30 +9x2x9 +8x1x29 +18x30x10 +18x14x5 +26x8x30 +12x1x1 +30x5x28 +26x17x21 +10x10x10 +20x7x27 +13x17x6 +21x13x17 +2x16x8 +7x9x9 +15x26x4 +11x28x25 +10x6x19 +21x6x29 +15x5x6 +28x9x16 +14x3x10 +12x29x5 +22x19x19 +25x15x22 +30x6x28 +11x23x13 +20x25x14 +26x1x13 +6x14x15 +16x25x17 +28x4x13 +10x24x25 +4x13x10 +9x15x16 +15x24x6 +22x9x19 +11x11x8 +4x19x12 +24x5x4 +27x12x13 +7x27x16 +2x6x9 +29x27x15 +18x26x23 +19x16x15 +14x5x25 +9x16x30 +4x6x4 +13x10x10 +1x8x29 +23x5x17 +19x20x20 +11x27x24 +27x15x5 +15x11x12 +21x11x3 +1x13x22 +17x8x8 +13x14x14 +17x22x7 +9x5x8 +2x6x3 +25x9x15 +11x8x13 +9x25x12 +3x16x12 +12x16x8 +16x24x17 +4x6x26 +22x29x11 +14x17x19 +28x2x27 +24x22x19 +22x20x30 +23x28x4 +16x12x14 +22x24x22 +29x1x28 +26x29x16 +3x25x30 +27x3x13 +22x24x26 +25x3x2 +7x24x2 +10x5x3 +28x8x29 +25x6x4 +12x17x14 +24x3x5 +23x27x7 +26x23x30 +11x10x19 +23x7x11 +26x14x15 +14x3x25 +12x24x14 +2x14x12 +9x12x16 +9x2x28 +3x8x2 +22x6x9 +2x30x2 +25x1x9 +20x11x2 +14x11x12 +7x14x12 +24x8x26 +13x21x23 +18x17x23 +13x6x17 +20x20x19 +13x17x29 +7x24x24 +23x8x6 +19x10x28 +3x8x21 +15x20x18 +11x27x1 +11x24x28 +13x20x11 +18x19x22 +27x22x12 +28x3x2 +13x4x29 +26x5x6 +14x29x25 +7x4x7 +5x17x7 +2x8x1 +22x30x24 +22x21x28 +1x28x13 +11x20x4 +25x29x19 +9x23x4 +30x6x11 +25x18x10 +28x10x24 +3x5x20 +19x28x10 +27x19x2 +26x20x4 +19x21x6 +2x12x30 +8x26x27 +11x27x10 +14x13x17 +4x3x21 +2x20x21 +22x30x3 +2x23x2 +3x16x12 +22x28x22 +3x23x29 +8x25x15 +9x30x4 +10x11x1 +24x8x20 +10x7x27 +7x22x4 +27x13x17 +5x28x5 +30x15x13 +10x8x17 +8x21x5 +8x17x26 +25x16x4 +9x7x25 +13x11x20 +6x30x9 +15x14x12 +30x1x23 +5x20x24 +22x7x6 +26x11x23 +29x7x5 +13x24x28 +22x20x10 +18x3x1 +15x19x23 +28x28x20 +7x26x2 +9x12x20 +15x4x6 +1x17x21 +3x22x17 +9x4x20 +25x19x5 +9x11x22 +14x1x17 +14x5x16 +30x5x18 +19x6x12 +28x16x22 +13x4x25 +29x23x18 +1x27x3 +12x14x4 +10x25x19 +15x19x30 +11x30x4 +11x22x26 +13x25x2 +17x13x27 +11x30x24 +15x1x14 +17x18x4 +26x11x3 +16x22x28 +13x20x9 +1x18x3 +25x11x12 +20x21x1 +22x27x4 +8x28x23 +7x13x27 +17x9x26 +27x27x20 +11x20x12 +26x21x11 +29x14x12 +27x25x1 +28x29x25 +21x23x28 +5x18x18 +19x5x4 +7x6x30 +27x8x11 +12x24x12 +16x25x22 +26x11x29 +25x22x17 +15x23x23 +17x9x6 +30x10x16 +21x3x5 +18x27x2 +28x21x14 +16x18x17 +4x18x2 +9x1x14 +9x1x9 +5x27x12 +8x16x30 +3x19x19 +16x26x24 +1x6x9 +15x14x3 +11x7x19 +8x19x3 +17x26x26 +6x18x11 +19x12x4 +29x20x16 +20x17x23 +6x6x5 +20x30x19 +18x25x18 +2x26x2 +3x1x1 +14x25x18 +3x1x6 +11x14x18 +17x23x27 +25x29x9 +6x25x20 +20x10x9 +17x5x18 +29x14x8 +14x25x26 +10x15x29 +23x19x11 +22x2x2 +4x5x5 +13x23x25 +19x13x19 +20x18x6 +30x7x28 +26x18x17 +29x18x10 +30x29x1 +12x26x24 +18x17x26 +29x28x15 +3x12x20 +24x10x8 +30x15x6 +28x23x15 +14x28x11 +10x27x19 +14x8x21 +24x1x23 +1x3x27 +6x15x6 +8x25x26 +13x10x25 +6x9x8 +10x29x29 +26x23x5 +14x24x1 +25x6x22 +17x11x18 +1x27x26 +18x25x23 +20x15x6 +2x21x28 +2x10x13 +12x25x14 +2x14x23 +30x5x23 +29x19x21 +29x10x25 +14x22x16 +17x11x26 +12x17x30 +8x17x7 +20x25x28 +20x11x30 +15x1x12 +13x3x24 +16x23x23 +27x3x3 +26x3x27 +18x5x12 +12x26x7 +19x27x12 +20x10x28 +30x12x25 +3x14x10 +21x26x1 +24x26x26 +7x21x30 +3x29x12 +29x28x5 +5x20x7 +27x11x2 +15x20x4 +16x15x15 +19x13x7 +7x17x15 +27x24x15 +9x17x28 +20x21x14 +14x29x29 +23x26x13 +27x23x21 +18x13x6 +26x16x21 +18x26x27 +9x3x12 +30x18x24 +12x11x29 +5x15x1 +1x16x3 +14x28x11 +2x18x1 +19x18x19 +18x28x21 +2x3x14 +22x16x5 +28x18x28 +24x16x18 +7x4x10 +19x26x19 +24x17x7 +25x9x6 +25x17x7 +20x22x20 +3x3x7 +23x19x15 +21x27x21 +1x23x11 +9x19x4 +22x4x18 +6x15x5 +15x25x2 +23x11x20 +27x16x6 +27x8x5 +10x10x19 +22x14x1 +7x1x29 +8x11x17 +27x9x27 +28x9x24 +17x7x3 +26x23x8 +7x6x30 +25x28x2 +1x30x25 +3x18x18 +28x27x15 +14x14x1 +10x25x29 +18x12x9 +20x28x16 +26x27x22 +8x26x1 +21x2x12 +25x16x14 +21x19x5 +12x9x22 +16x5x4 +5x4x16 +25x29x3 +4x29x13 +15x16x29 +8x11x24 +30x11x20 +17x21x14 +12x24x10 +10x12x6 +3x26x30 +15x14x25 +20x12x21 +13x11x16 +15x13x3 +5x17x29 +6x3x23 +9x26x11 +30x1x8 +14x10x30 +18x30x10 +13x19x19 +16x19x17 +28x7x10 +28x29x4 +3x21x10 +4x28x24 +7x28x9 +2x4x9 +25x27x13 +6x12x15 +4x18x20 +20x1x16 +5x13x24 +11x11x10 +12x9x23 +1x9x30 +17x28x24 +9x5x27 +21x15x16 +17x4x14 +8x14x4 +13x10x7 +17x12x14 +9x19x19 +2x7x21 +8x24x23 +19x5x12 +11x23x21 +13x3x1 +5x27x15 +12x25x25 +13x21x16 +9x17x11 +1x15x21 +4x26x17 +11x5x15 +23x10x15 +12x17x21 +27x15x1 +4x29x14 +5x24x25 +10x10x12 +18x12x9 +11x24x23 +24x23x3 +28x12x15 +29x9x14 +11x25x8 +5x12x2 +26x26x29 +9x21x2 +8x8x25 +1x16x30 +17x29x20 +9x22x13 +7x18x16 +3x3x23 +26x25x30 +15x23x24 +20x23x5 +20x16x10 +23x7x8 +20x18x26 +8x27x6 +30x23x23 +7x7x24 +21x11x15 +1x30x25 +26x27x22 +30x28x13 +20x13x13 +3x1x15 +16x7x1 +7x25x15 +12x7x18 +16x9x23 +16x12x18 +29x5x2 +17x7x7 +21x17x5 +9x9x17 +26x16x10 +29x29x23 +17x26x10 +5x19x17 +1x10x1 +14x21x20 +13x6x4 +13x13x3 +23x4x18 +4x16x3 +16x30x11 +2x11x2 +15x30x15 +20x30x22 +18x12x16 +23x5x16 +6x14x15 +9x4x11 +30x23x21 +20x7x12 +7x18x6 +15x6x5 +18x22x19 +16x10x22 +26x20x25 +9x25x25 +29x21x10 +9x21x24 +7x18x21 +14x3x15 +18x19x19 +4x29x17 +14x10x9 +2x26x14 +13x3x24 +4x4x17 +6x27x24 +2x18x3 +14x25x2 +30x14x17 +11x6x14 +4x10x18 +15x4x2 +27x7x10 +13x24x1 +7x12x6 +25x22x26 +19x2x18 +23x29x2 +2x15x4 +12x6x9 +16x14x29 +9x17x3 +21x9x12 +23x18x22 +10x8x4 +29x2x7 +19x27x15 +4x24x27 +25x20x14 +8x23x19 +1x24x19 +6x20x10 +15x8x5 +18x28x5 +17x23x22 +9x16x13 +30x24x4 +26x3x13 +12x22x18 +29x17x29 +26x4x16 +15x7x20 +9x15x30 +12x7x18 +28x19x18 +11x23x23 +24x20x1 +20x3x24 +1x26x1 +14x10x6 +5x27x24 +13x21x12 +20x20x5 +6x28x9 +11x26x11 +26x29x12 +21x4x11 +20x11x17 +22x27x20 +19x11x21 +2x11x11 +13x5x7 +12x10x25 +21x28x1 +15x30x17 +28x19x1 +4x19x12 +11x4x12 +4x10x30 +11x18x5 +22x20x12 +3x7x27 +20x26x4 +13x27x26 +23x14x13 +4x19x7 +26x27x16 +20x5x20 +18x5x8 +19x21x1 +22x8x1 +29x4x1 +24x10x15 +24x9x20 +10x3x8 +29x30x3 +2x8x24 +16x7x18 +2x11x23 +23x15x16 +21x12x6 +24x28x9 +6x1x13 +14x29x20 +27x24x13 +16x26x8 +5x6x17 +21x8x1 +28x19x21 +1x14x16 +18x2x9 +29x28x10 +22x26x27 +18x26x23 +22x24x2 +28x26x1 +27x29x12 +30x13x11 +1x25x5 +13x30x18 +3x13x22 +22x10x11 +2x7x7 +18x17x8 +9x22x26 +30x18x16 +10x2x3 +7x27x13 +3x20x16 +9x21x16 +1x18x15 +21x30x30 +4x25x23 +3x11x7 +5x6x12 +27x1x20 +13x15x24 +23x29x2 +13x5x24 +22x16x15 +28x14x3 +29x24x9 +2x20x4 +30x10x4 +23x7x20 +22x12x21 +3x19x11 +4x28x28 +5x4x7 +28x12x25 +2x16x26 +23x20x7 +5x21x29 +9x21x16 +9x6x10 +9x6x4 +24x14x29 +28x11x6 +10x22x1 +21x30x20 +13x17x8 +2x25x24 +19x21x3 +28x8x14 +6x29x28 +27x10x28 +30x11x12 +17x2x10 +14x19x17 +2x11x4 +26x1x2 +13x4x4 +23x20x18 +2x17x21 +28x7x15 +3x3x27 +24x17x30 +28x28x20 +21x5x29 +13x12x19 +24x29x29 +19x10x6 +19x12x14 +21x4x17 +27x16x1 +4x17x30 +23x23x18 +23x15x27 +26x2x11 +12x8x8 +15x23x26 +30x17x15 +17x17x15 +24x4x30 +9x9x10 +14x25x20 +25x11x19 +20x7x1 +9x21x3 +7x19x9 +10x6x19 +26x12x30 +21x9x20 +15x11x6 +30x21x9 +10x18x17 +22x9x8 +8x30x26 +28x12x27 +17x17x7 +11x13x8 +5x3x21 +24x1x29 +1x28x2 +18x28x10 +8x29x14 +26x26x27 +17x10x25 +22x30x3 +27x9x13 +21x21x4 +30x29x16 +22x7x20 +24x10x2 +16x29x17 +28x15x17 +19x19x22 +9x8x6 +26x23x24 +25x4x27 +16x12x2 +11x6x18 +19x14x8 +9x29x13 +23x30x19 +10x16x1 +4x21x28 +23x25x25 +19x9x16 +30x11x12 +24x3x9 +28x19x4 +18x12x9 +7x1x25 +28x7x1 +24x3x12 +30x24x22 +27x24x26 +9x30x30 +29x10x8 +4x6x18 +10x1x15 +10x4x26 +23x20x16 +6x3x14 +30x8x16 +25x14x20 +11x9x3 +15x23x25 +8x30x22 +22x19x18 +25x1x12 +27x25x7 +25x23x3 +13x20x8 +5x30x7 +18x19x27 +20x23x3 +1x17x21 +21x21x27 +13x1x24 +7x30x20 +21x9x18 +23x26x6 +22x9x29 +17x6x21 +28x28x29 +19x25x26 +9x27x21 +5x26x8 +11x19x1 +10x1x18 +29x4x8 +21x2x22 +14x12x8 diff --git a/tests/aoc/2015/day03.txt b/tests/aoc/2015/day03.txt new file mode 100644 index 0000000..f7791cc --- /dev/null +++ b/tests/aoc/2015/day03.txt @@ -0,0 +1 @@ +v>vvv>v<<<^^^^^<<^^>v^>^>^>^>^>^<<^><<<^vvvv>^>^><^v^><^<>^^>^vvv^>^>^^<>><>^>vvv>>^vv>^<><>^^>^>><<^><><>^<^>>vvv>v>>>v<<^<><^v>^^v^^^<^v^^>>><^>^>v<>^<>>^>^^v^>>><>v^v<>>^>^<>v^>^<>^v^^^v^^>>vv<<^^><^^>^^<^>>^^^^^v^vv<>>v^v<^v^^<><^<^vv^><>><><>v>vvv^vv^^<<><<<^v^>vvv^<^>vvvv^>^>>^v^v>vv^<>><^^^>^>>v>^>v^<>v><^<^^^vv<^^<>v^v^vv<>>>>v^v<>><^^v>vv^^>v^v>v>v>>vv>^^>^v><<^<vv^^^v>v^^^>><^^>v>^^v>>v^^^<^^v>^v>><^<^<>>v<<^^vv>^^^v<^<^^vv^>>v^>><<<>^vv^<^<>v^^<<^><>>^^^<^vv<^^^>><^^v>^^v^^^^<^v<^<<<<^v^<^^<>^^>^><<>>^v><>><^<^^^>>vv>^>^^^^^v^vvv><><^<^>v>v^v^>^><><^<^><>v<><>^v^^v>^<<<>^v^>^<v^<>>^vv>v>>>^<^>>>>>v>>^v>v><>>vvv<^^><<^>^>v<^vvvv<^^^v^^^>v^v<>v<^^v>>><>v>v>>^^<^^v><<<<<^vv<^<>^>>>^v>^v>vv>^v<>v>v<^>>v>>^>^><^^v<><><^^>^^^^>vv^v^v>^v^^v^^v>><^v>>vv<>vvvv<>>^v^>^>>v^v^<<>>^v<^^vv^><>v>^>v><<<<<<<^>^^v^<<^^>>vvv^<><>><>^^v<<^^v<^^>v^>>>v^v>v^><>v<<>v>^^v><<<<><^v^v>>^<>^<<>^>v<<>><^<<<<^v>^<^v>v>vv^>v<^<<>v^v>><v>v>>v^vvv^^>>>v^<^<<^^<<<>v^<v<^^<>^>v>>v<>^>^^>>^v<<>v^^^>>>^vv<^v^>v>^><>v^^<>^^v^^vv^<^>^<<>><<^>^v>>><<<<><<^v>v^<^><^<>>v^>^^^<>>v<>>^>>v^><<>vvv><^>>v><>v>>^>v><<><<>^<>^^^vv>v^^>>^>^<^vv^>v^>^><^<^><><v<^^v^^<<<<^><^^<^><>>^v<<^<<^vv>v>>v<^<^vv>>v^v<>^>v<>^v<<>^^v>>>v^>^v^v>^^^v><^>vvv^<<<>v<>v>^>vv^<^^v^><^^^^^v<^>>vv^v^>^^<>>><^v^<v>^v>^^v<>>vv>>^v>>^<<<<^><<<><^^>>v<>^vvvv>v^^^>^^^>^<^^vv<^v^v<v^^<>^>^<^v>vvv><<^><><^^v<<><^^><>^v>^<><<^<^^<<>vv<>^^<<^>><<<>>vvv>^>v^^v^><<^>v>^>^<^<<>v<^>vv^v^v<>vv<vv<^>v^<>^vv^v^>>>v^v><^<><<>vv^>vvv^>v>>><^^vvv<^<^>>^^>^^vv>>><^v<>^v^<<>v^^^^>>^<^>^v<^^^^v>^>>v>^>^>>>v^<<^>^<<^^<>v<^v<^<>v^v>^^v^vv>vvv>v^<^>>vvvv<>>^^<>v^<><>v<^<>v<>^>v<>vv>v<^^>v>><>>^<^^<>>^>^>vvv^v>>^>^>v><><<>v<>^v<^vv^^^<>^^<<^^^v<>>v^>vvvv>^^v^>^>^<<><^^^^<<>^<>vv^<><^>^^<>v^<>>>v><>vvvvv>v>v^^>^<vv>>v<<^<>^^^v^<><>>^<<>>><>v>^>^^^^vv^^<<><^^<v>vv<<<^<v<>>^<^>^>>v>><^^<>><<<><<><^<^v<^^v<<>><<<<^>v^>v^v^<<>>v<><^<>><>>^><>v^v>v<<>v<>v^^><<>>>v<<>>>>^>v>>>>vv>v>^<^^^<>v^<^^v^vvv^>vv>^^<<>vvv<<^^<^>^>>v>v<<<<<>^^vv^>>v>^<^^v>>v>^v<><>^<^>v>v<<<^^^v>^<<<>vvv^v^^>^>>^>v>v<>^^><>>v>^>v<<<^^^v^><><^<^<>>^v>vv<^v<<^vv>v^<v>v>^v^>^v<<^v^vv>v^<<>>v<>>vvv^^vv^^v><^>v^vv<^^<<>v<><^><>^<><vv<>^vv>v><^^v<>><^v^v><><>>vv<>>>><<^>>><^^^vvv<<><>>>v<<<<<>v^^<<^vv^>vv>^<>^v^^<>^^^vv>v^^v>^v>^<>v^^^>v^^v<^>v^v^<>v>v>v<^^vv^v<^^^^vv<<><<^>>^^<v^>>^^^><^^>^v^v>^<^>>^v<>^<^>v>^>^v^<^^^<^vv<^^>>v^>^v^>>>>^v>^^<<^<^^v^<<<>>><>^<>>>v<<><<^^<^^>v^>^>v^v<><^^v>^<^v^v>>>^^<^^vv<<^><><^<>v>>>vv>><^^^v^^^v<^^v>v<>>^^<><>v>^^>>>><>v>^v>^vv^v>^>^^^><>^<<>>><<<><>^^<<v^>v>v^^^>^>^v<<>v>vv>><<^^^>>^><^>v<^<^v>><^^>v<><>^><<><>v^>v<><^^>><>^<^^v<^<><<<^^<><>>>^>v^<><^<<^vv<^v^v^v<>v^^v>v^<^>^vv^>>><<>v^vv^<>^v^><v^<><>>v^v^><>v^vvv^^^<<^<>v^v>^^><>v>>v^<>^>v>^>><<>v^v><^v>v>>><^<^^>vv<^>^<^>^^v><><^<<^^vvv^v>^>^<>>vv>v^^v^^vv<^^>><^v>v^<vvv<>>^v><<>^v<<<>^><^vv><<^^v<^vv<>^v>>>><<<<^^<^v>^<^^<^<^^>>^^v>^^^^v^^^<<>^^vv<><^<<><>^>v<>>v^^^>^v^^v^v>>>>>^v>^>^^v>><^^^v<<^^>^<^<^><^<<>v>v>^v<><^>vv^vv><^><>^><<^^>v>v>^^^<>v>^v^^>vv^vv<^^>><>^>^<>v>><>^v<v>^><^^^v^<<^v^>v^>vv>v^<>v><^v>v<>^v<>^v>^^<>vvv^>^<><^>><^<>^v<<^v^><<^<^v>^vv^v>v<^^vv<><>vv^>v<<>v>v>^^>>><<<^>^vv>>^^^>v<^vv<>v<<>>>^<^^^^>v<^^<>v>vvv^>v>v<^>^v^<>v>>vvv>^^><^vvv>><>>>^<<^>>v^^>>^><>v<^^v^<<>^<>>><^v^v>>>^vvvv^<><<>v>^v^v>v><^<<^>^^>v<^v^<^>v>^<v^<>v^<>vv^<>^>^>v^>^vv<>^^<<>>v<>^v<><><<v>v^>vv^><<<^v<><>^^v^^v^^^>^<>>>>^><>>v>>v<<>v<<<<<<<^v<<^^^v<^v<>v^^<<<^<>>v^vv^<^^<^^<<^>vv><^<^^v<<<^><^v<^><>v^v^^>>><<^<<^<>>^>>^<<<>>v>^v>><>v>>v>><>v>><^^>^^vv<^^<^>vv><<^>><<>^vvv><^v^>vvv^>>^<><^>^<<>>v^v>v<<>^>>^>v<^^<^<<>^^v^^v>v<>^<^^<v^^vvv^^v>^vv^<>v<^v^>^vv<v^<<^>^><^^<^^<^>vv^<>^<>^>^^<^v><<<^>vv^vv>v^v<>^^v^<^^^vvv^>v^<><>v>vv<^v^>>^v<^^vv>vv>^>><<<<>^><>>v<>>v>^v<^vv>^^>^<^<>v^v<^^v<^^>^^<>^^^^>^vv<^>><^>vv^>v^>^vv>^>v^^<>>^v<>>v<^>^v>vv^>^>>>vvv>vv>^><^v<<<>^^v>v^v<^^^v^^>^><<^^>^><^^^^^^^<^v<^>>vv>>^v^vv<>><>^>>>^^^^^><^<<^v<>vv^>>v<^vv<^v<>v<>^v^<<>>>>v^^>^vv<<><<>v^v<^<^>>^^><^>^><<><^<><>vv>>>>^><<^^^<^v^>^>^^>^<^><^^<^^<>><>><<<>^>^^v<>^<<>vv>^>>^>^<>>vv<^^vv<>v<>^^>^v^v^v>^^^v<<<^vv^><>^>^^vv>v^<<^><>>vv^^^^^>v>>v<<<>^<><^v<^v<^>^<>^vvv>^>v><<v>vv^<^^>v^v>^<^v^<^v<<^>^<><>^^<>>^^<^v^<^<<^>v^^>v^v^^^<^v<<^v>^>>^^^^^><<>v^><>v^^<^v<^<v^^^><^^^><<<<<>^<<^<>>v<^>><^^^<>>>>vvv>v<>>>v^v^v<^<<^>^<<>v>>^>^^><^><<^v^^<^<>v^v>vv<>>>>>>v<<>>^^>v<<<>^<<^><<^v>vv>>>><><>v^<^v><^>v>>^^^v<^>>^>>v<<^<<>vvv>>^v<>>^v><<<^v^v<>^vvv^^^<>vvv^^^>>v>^>^<><<>vv>^v^>>^<v><><<>^^><>^<<>^v<<>>v^vv<<>^^v^v^v><^>v>v<^<<^<^>vv>^v<<^>^>>v^<v^^v^^>><<<>^v>><>v<>><^v>^^v<<<<^v^vv<<<<><><^<^<^v><<^^v^<<<<<^^><^^>vvv<^><>vvv^v^>^>^^^v<<^<^^>vv^vv^><^v^<<>v<^^>^vv<<>^<<><^>v^<<^<>v><><>v<<^^><^^^v>>v>^vv>^v^^<><<<<<^>^v^<^<^^>^vv<^>v^^v^<>v<>v^v>vvv><><<><>vv^^>^^^<><^>^^^>vvv><>v<>>v^>v^^vv^>v>>>><^^>^v^v>>vv<^>><<<^>><^<^>^<^>^>>v^<^<>^<^^<>^<>>><^<^<^<<^><^^>vv<>^^>v^>>v>>>v<<^vv^<><>>>^^<^v^>>^>>><<^<>^>>^v>>><^^^<<^vv><><<>^^^<>^^^>><>>>^>vv>^<^<>>^<^^>v^>vv><><>>><><<^^v<<^vvv<><><<^v>^v<>^<^^^v^>^<^><^v>v>^v<>><^^v^^^^^<>>vvvv>>>>^<<><^v>vv>>^^><<><><^^^<^<^<<^v>^^^>>>>><v^^^<>>vv^^^v<><^>v>><<><>v<^><<>>><>v>^<>>^>v^v<<<<>^vv<<>>>>>vv<><>^<^v>vv^<>><<>^<>><^>>>><<>^^>><<<^^^^^v>>^<<>>vvvv<^v^vvv<<<^><>>>>vv^<^v>v<^<>^v>>^<^^v^>>><>^^<^v>>v<<>vvvv>^><>v^<>^<<^vv<^>>^v^>^^<<<^>>^^>^<^^<^<<v^^v^^<^v<^>>><<>vv<<^><^>vv<^>>^vv>>>^>>><^<<<>>^<<>><^<<^^^>>v^^>v<<<>v>v>v^<>>>^vvv><<^^<<><^v>>>>vv^^v^v<>v>v<<<<><<>vv<><^^^<>>v>>>>^^<><^<^v^>>^^v>^<v>^^>^v^<>>v^^<^v^^<<>^^>v^^>><<<<^<^^v>^^v>v<^>v^<>vv>>^^v>v^^>vvvvv<<>vv>vvvvvv>>v>>^^^vv^^><>v^^^^v>vv>v<^v>>>>^>^>^v>^>>><<>>^vv>>>><><<^<^><^vv^v>>>>>v>^>^^v^>v<^v^<^<v<>>^vv<<>^v^v>><><<>>v^^<<>^^<>v<<^^<^^>^^>^<^><>>v<>>^^<^>><<>>^v^>v>v<<^^<<^>v>v^^v^^<<>^v>v>v<^^>^v<><^<<v<^<<<<^^>v^v^^><<><^^^v^^>>>vvv><>vv<>>^^v^v<<^>v^^v^>vv>^<^^<^v^^<^^v<<>>vv<^>>^><><>v>>v<>^<<>>> diff --git a/tests/aoc/2024/day01.k b/tests/aoc/2024/day01.k new file mode 100644 index 0000000..fbbc49c --- /dev/null +++ b/tests/aoc/2024/day01.k @@ -0,0 +1,7 @@ ++/{x|-x}[-/{x[0)) & (&/ad<4) & (&/ad>0)})' i:.'0:"day02.txt" /part1 = 516 + ++/(safe2:{|/safe'x(&'(,5#1),~=5)})'i /part2 = 689 INCORRECT +/689 incorrect - right answer for someone else +/794 too high + ++/(safe2:{$[safe x;1;1=+/safe'x(&'~=5)]})'i /part2 = 613 incorrect + + +1 0 0 0 0 1 ~ safe'(7 6 4 2 1; 1 2 7 8 9; 9 7 6 2 1; 1 3 2 4 5; 8 6 4 4 1; 1 3 6 7 9) / test1 correct +1 0 0 1 1 1 ~ safe2'(7 6 4 2 1; 1 2 7 8 9; 9 7 6 2 1; 1 3 2 4 5; 8 6 4 4 1; 1 3 6 7 9) / test2 correct + + + +/ attempt 2 ========================== + +safeinc:{*/(4>d) & 0d:1_-':x} ++/ (safe:{(safeinc x)|safedec x})'i /part1 = 516 + ++/(safe2:{|/safe'x(&'(,5#1),~=5)})'i /part2 689 INCORRECT diff --git a/tests/aoc/2024/day02.txt b/tests/aoc/2024/day02.txt new file mode 100644 index 0000000..cd46e29 --- /dev/null +++ b/tests/aoc/2024/day02.txt @@ -0,0 +1,1000 @@ +16 19 21 24 21 +15 18 19 22 24 25 25 +80 81 83 84 87 89 93 +6 7 8 9 10 13 18 +60 62 61 64 66 67 +76 79 81 84 82 80 +70 73 72 74 74 +67 68 71 74 73 77 +56 57 60 59 61 64 67 74 +37 38 39 40 40 43 +90 92 95 95 96 97 94 +80 83 86 86 86 +44 47 49 49 51 54 58 +69 71 74 74 81 +66 68 72 75 77 +34 35 39 41 38 +58 60 63 67 70 72 72 +43 46 47 51 52 53 56 60 +35 36 37 41 44 50 +63 64 67 69 71 72 78 80 +19 22 23 30 28 +20 21 24 30 30 +75 78 80 83 90 91 95 +16 17 20 22 23 29 31 36 +22 21 24 26 28 +87 84 87 88 86 +48 46 48 51 51 +40 37 40 43 44 48 +77 75 78 79 81 84 86 91 +43 41 40 42 44 +32 30 31 32 35 32 29 +87 84 81 83 83 +43 41 44 47 48 45 49 +49 48 51 53 54 57 54 61 +68 66 69 69 72 75 77 +9 7 8 10 10 12 11 +77 74 77 77 77 +5 2 4 4 8 +34 33 36 36 42 +11 9 13 15 18 21 +22 21 22 23 27 24 +68 67 70 74 77 80 82 82 +87 86 87 91 95 +26 23 27 28 30 35 +23 20 21 28 31 +85 83 89 92 95 96 94 +32 31 38 40 40 +40 38 40 41 43 49 51 55 +22 19 21 23 29 35 +86 86 89 91 94 96 98 +72 72 73 75 77 78 76 +42 42 45 48 49 49 +41 41 43 46 48 49 53 +35 35 37 39 41 43 48 +85 85 86 83 86 87 88 91 +20 20 19 20 21 20 +13 13 14 13 15 18 18 +48 48 50 48 50 54 +26 26 23 25 30 +62 62 64 66 66 67 +31 31 32 34 35 35 38 37 +11 11 14 14 14 +6 6 9 12 12 13 14 18 +5 5 5 7 9 14 +87 87 91 92 95 96 99 +61 61 65 67 64 +77 77 78 82 85 85 +48 48 52 53 57 +69 69 70 74 81 +29 29 30 32 35 37 44 46 +52 52 54 56 57 63 64 62 +42 42 45 47 48 51 57 57 +57 57 60 65 69 +28 28 33 34 36 38 44 +41 45 48 50 52 55 58 +79 83 84 85 86 83 +69 73 74 77 79 81 81 +14 18 21 22 24 28 +26 30 33 36 43 +8 12 13 12 14 16 17 +84 88 91 92 89 92 94 92 +30 34 32 34 37 37 +3 7 5 6 9 10 14 +31 35 37 35 36 39 45 +59 63 65 66 67 67 68 +13 17 18 21 21 18 +72 76 78 79 82 82 82 +4 8 9 9 10 13 14 18 +42 46 49 49 52 57 +17 21 24 28 29 +64 68 69 73 75 78 77 +57 61 63 67 67 +11 15 19 22 26 +78 82 83 87 94 +34 38 45 47 48 49 52 +44 48 55 56 55 +45 49 52 57 59 60 63 63 +48 52 55 62 66 +14 18 20 27 34 +9 15 18 20 23 25 +4 10 11 12 14 17 18 16 +4 10 12 13 15 17 17 +18 23 24 25 27 31 +27 33 35 37 40 42 47 +16 22 24 21 22 25 27 +42 49 52 49 47 +39 44 45 43 44 46 49 49 +29 36 39 42 45 46 43 47 +37 42 43 42 45 48 53 +54 60 63 63 65 +66 71 73 76 76 79 77 +56 62 62 64 66 66 +69 76 79 82 83 83 87 +77 84 84 87 93 +76 81 85 87 89 91 94 +70 77 81 82 85 87 89 87 +14 20 22 26 28 28 +56 61 65 68 72 +78 85 86 90 95 +22 29 30 36 39 40 42 44 +18 25 27 28 35 38 39 38 +15 21 24 25 27 34 34 +21 26 27 33 36 40 +42 48 49 51 52 57 64 +97 96 94 91 88 89 +74 71 68 67 66 66 +67 64 61 58 57 53 +63 61 60 57 54 52 51 44 +41 40 37 35 34 35 33 +53 50 52 51 52 +31 28 25 24 23 20 22 22 +25 24 27 25 24 21 18 14 +82 80 78 81 78 76 70 +36 33 32 31 31 30 +66 65 65 64 63 61 59 61 +82 79 79 76 75 72 72 +94 92 90 90 87 85 81 +22 19 19 18 13 +39 38 35 34 31 29 25 24 +72 70 67 63 62 59 58 59 +28 26 23 19 19 +24 22 20 16 13 10 9 5 +63 62 58 57 52 +27 25 23 21 16 14 13 11 +76 74 72 65 64 65 +25 23 17 14 14 +78 77 76 69 68 67 64 60 +78 76 70 68 66 65 63 58 +63 66 64 63 62 61 60 57 +61 63 60 57 54 53 56 +90 92 89 88 85 83 82 82 +41 42 39 38 36 35 33 29 +15 17 15 12 11 9 3 +77 79 78 77 80 79 76 +43 46 44 45 44 41 42 +27 30 33 32 32 +18 19 16 13 16 12 +16 17 14 12 10 8 11 6 +77 80 80 78 75 +31 34 34 32 34 +3 6 5 5 2 2 +76 78 75 72 69 69 65 +43 45 43 43 41 35 +36 39 38 35 33 29 28 +81 84 81 77 75 78 +40 42 38 36 33 32 29 29 +29 31 29 28 25 24 20 16 +80 82 79 76 75 71 69 63 +49 52 50 47 46 41 39 +78 79 78 77 72 69 66 67 +12 13 11 5 5 +12 15 14 9 8 7 6 2 +44 46 45 43 38 36 30 +61 61 58 56 53 52 50 47 +35 35 32 31 28 30 +46 46 44 42 42 +80 80 77 76 72 +63 63 60 59 56 55 49 +77 77 80 78 75 73 +52 52 51 52 53 +12 12 11 9 10 10 +60 60 63 61 58 54 +67 67 69 67 62 +95 95 93 93 92 91 +53 53 52 52 51 48 51 +67 67 67 66 63 63 +11 11 10 7 7 3 +26 26 23 21 20 20 13 +80 80 78 74 73 72 70 67 +54 54 53 49 52 +63 63 62 59 55 53 53 +61 61 57 55 51 +69 69 68 66 62 55 +85 85 82 75 74 73 72 +47 47 42 40 39 37 34 36 +60 60 59 58 55 52 47 47 +77 77 75 69 68 64 +53 53 52 45 44 43 36 +43 39 36 33 30 29 26 23 +65 61 60 57 54 56 +55 51 48 47 45 42 42 +52 48 47 45 43 41 37 +29 25 24 23 22 17 +38 34 37 35 32 29 28 26 +24 20 19 17 15 14 17 20 +33 29 28 25 26 26 +38 34 35 32 31 27 +33 29 27 24 23 24 23 17 +36 32 32 31 29 26 +13 9 8 5 3 1 1 4 +6 2 1 1 1 +78 74 73 72 72 70 69 65 +44 40 40 37 30 +41 37 33 30 28 +88 84 83 79 78 75 73 74 +12 8 4 2 1 1 +65 61 57 54 50 +85 81 77 74 68 +76 72 66 64 62 +97 93 90 87 81 84 +64 60 53 51 48 48 +56 52 50 45 43 39 +56 52 49 43 40 39 36 30 +60 53 51 50 48 +36 31 29 26 25 24 26 +55 50 48 47 44 42 41 41 +90 85 83 81 77 +89 82 80 77 70 +56 49 52 49 47 +9 3 5 3 6 +9 4 5 4 4 +18 12 14 12 8 +66 59 58 56 53 54 49 +97 90 88 87 84 82 82 80 +60 55 53 52 52 54 +12 5 5 4 4 +26 19 19 16 15 11 +68 62 60 57 56 56 54 47 +59 52 48 46 45 42 +21 16 12 9 12 +99 92 91 88 84 81 81 +58 52 49 45 43 42 38 +48 41 38 36 35 34 30 23 +31 26 24 18 15 13 +18 13 11 6 8 +99 93 88 85 84 84 +85 78 75 73 68 64 +86 81 75 72 66 +5 9 7 9 6 +22 26 29 32 29 31 34 34 +22 29 30 32 34 39 45 +52 45 43 41 40 37 32 34 +30 32 34 39 43 +11 16 18 22 22 +68 72 75 76 79 82 86 85 +69 74 76 80 84 +65 65 63 61 57 55 53 48 +26 25 20 17 15 12 9 +27 29 28 23 21 16 +49 53 54 57 59 62 66 +72 72 73 80 82 85 +44 47 49 50 54 55 57 57 +20 19 17 19 21 +18 18 22 24 26 29 31 32 +48 52 54 54 56 56 +72 76 79 78 84 +64 60 57 53 48 +48 44 42 40 37 33 32 35 +52 52 48 45 44 41 38 40 +70 76 78 80 80 87 +55 54 52 48 44 +39 38 32 31 30 28 22 +8 8 5 1 1 +43 48 50 53 57 +29 32 29 27 28 26 27 +44 49 51 53 60 61 65 +16 17 10 8 8 +36 31 28 25 22 20 20 17 +61 54 51 44 42 39 35 +96 90 88 84 83 80 79 +55 58 57 53 50 50 +41 46 48 50 48 54 +7 11 12 14 14 17 23 +79 82 81 77 74 77 +34 28 24 21 22 +77 81 84 86 87 90 94 94 +67 74 77 79 80 83 87 90 +94 90 89 84 80 +65 64 63 60 60 +40 44 47 48 52 53 55 +35 35 34 33 32 29 27 29 +75 82 85 88 90 89 88 +92 88 88 87 86 83 80 +99 92 91 86 84 +11 9 6 2 5 +57 57 50 48 44 +21 18 19 23 28 +30 33 31 31 30 27 23 +22 22 22 19 14 +61 57 54 54 52 45 +81 81 84 82 80 +93 93 90 88 91 90 87 83 +29 36 40 41 47 +84 83 84 86 86 88 +33 37 38 45 52 +56 60 64 65 66 67 71 +98 98 97 96 96 94 94 +96 89 86 84 77 74 68 +51 45 44 43 36 36 +66 63 70 73 79 +84 79 76 74 71 71 70 70 +27 23 22 19 14 13 10 5 +91 84 83 82 80 76 +13 14 17 20 21 26 26 +96 97 96 93 89 88 86 +39 45 46 43 44 45 +87 80 76 75 72 72 +86 88 89 90 92 93 92 91 +45 45 48 50 57 54 +65 63 65 67 70 67 +38 34 32 32 30 27 25 21 +17 21 23 20 22 23 26 28 +65 66 71 74 75 +6 6 9 12 15 18 22 +76 78 76 78 80 81 88 +32 25 24 22 21 21 +49 50 52 54 54 56 59 59 +16 13 16 19 20 27 29 +71 71 73 71 70 +75 69 66 66 65 58 +58 55 57 60 61 64 66 73 +52 53 53 51 48 47 44 +28 21 19 17 15 11 7 +57 60 61 64 61 63 65 69 +8 5 8 9 10 14 +65 58 56 56 53 51 54 +68 67 69 76 80 +21 21 21 18 17 14 12 8 +67 66 68 66 63 60 54 +26 27 25 20 19 17 14 16 +31 30 33 34 34 +53 55 59 62 60 +96 92 95 93 90 89 83 +13 18 20 23 26 25 29 +36 36 36 35 36 +61 61 58 56 52 49 46 +38 35 38 41 38 40 39 +11 14 13 10 8 2 +11 14 15 21 18 +50 53 53 51 48 47 47 +27 25 23 25 23 26 +48 48 45 44 45 42 45 +66 62 61 59 54 51 53 +6 6 8 5 7 8 9 14 +67 67 65 68 69 72 72 +66 64 65 66 73 70 +94 92 94 95 97 99 +90 89 86 85 84 82 82 78 +77 77 81 84 87 87 +31 36 38 36 39 41 42 42 +73 69 68 67 68 66 66 +14 12 11 10 7 4 6 6 +29 25 23 22 19 17 16 10 +80 81 80 78 79 79 +9 7 7 9 10 7 +88 84 81 79 79 79 +27 28 29 32 33 35 38 42 +13 11 8 8 6 5 8 +30 37 44 47 48 +53 54 61 62 65 68 74 +54 54 51 44 41 38 35 35 +38 38 35 30 28 25 28 +83 90 93 96 94 +70 74 75 77 80 81 82 +71 74 71 70 68 67 68 +35 35 33 36 39 41 45 +32 29 27 23 23 +45 49 51 53 56 61 61 +20 20 18 19 13 +64 60 58 55 52 +2 2 2 5 6 10 +68 69 67 65 63 63 +67 71 74 76 79 79 +42 37 34 32 31 29 28 26 +24 28 31 35 37 40 42 49 +79 77 80 80 80 +58 51 50 50 47 46 42 +23 28 35 37 40 43 43 +26 30 31 33 32 35 39 +53 55 58 58 62 +88 82 80 78 75 77 75 +70 68 69 70 70 72 79 +27 29 31 31 34 36 38 39 +3 4 5 6 7 9 9 15 +46 45 41 38 37 31 +51 52 52 55 56 59 56 +69 68 69 73 73 +78 84 86 86 88 89 93 +14 7 9 8 6 3 6 +28 26 24 22 19 16 15 11 +86 86 81 78 75 74 +50 43 39 37 35 30 +54 60 64 65 66 63 +54 54 51 48 45 +74 74 74 77 80 81 82 81 +21 21 18 14 11 9 5 +49 54 55 57 57 +79 78 77 75 74 71 70 64 +15 15 18 20 22 +98 91 90 89 87 86 89 83 +95 91 90 86 83 +69 67 71 73 76 80 +57 57 55 52 52 +74 74 75 78 82 79 +47 43 45 43 41 40 38 39 +70 74 76 78 79 79 83 +88 88 90 93 94 93 +83 83 85 86 86 +55 56 57 58 62 64 67 68 +86 86 91 94 94 +75 76 77 74 77 80 83 86 +51 58 58 61 64 67 68 68 +67 63 62 61 59 56 54 50 +97 91 89 88 87 86 79 +54 47 45 48 47 44 42 38 +22 19 22 19 20 22 26 +29 26 24 23 20 13 11 11 +3 3 4 6 10 16 +52 56 57 62 66 +94 96 96 93 88 +70 70 70 73 76 79 79 +36 35 38 37 36 33 31 +31 31 32 35 36 41 +17 14 12 11 8 6 9 5 +96 92 91 84 81 81 +76 72 68 66 65 65 +1 4 6 7 11 12 16 +57 52 51 50 52 51 51 +1 5 6 7 9 12 15 14 +66 66 69 68 70 +47 43 42 35 33 +59 57 59 64 65 67 67 +19 19 19 21 24 26 28 +51 51 49 47 40 +52 50 47 47 47 +92 89 89 86 81 +34 30 33 32 29 25 +6 10 13 14 15 20 +51 47 46 44 40 37 36 32 +62 58 58 56 53 50 52 +26 26 24 23 20 19 15 +36 34 33 32 25 24 23 26 +53 54 55 56 56 +29 29 30 27 25 25 +70 75 77 79 85 +18 18 16 16 13 10 +80 85 87 90 91 94 +56 59 56 56 54 52 55 +22 18 20 18 17 16 +12 10 11 14 18 20 21 22 +94 96 93 86 85 83 +54 56 54 52 51 53 51 49 +72 75 71 69 66 64 58 +41 45 46 47 54 56 59 +71 71 75 77 81 +14 17 16 13 15 11 +4 4 9 10 12 16 +71 72 73 76 79 76 +67 69 66 67 65 58 +32 34 31 30 29 28 25 23 +38 35 35 33 30 +48 49 52 55 58 59 62 67 +45 51 52 52 54 +28 34 36 38 38 36 +12 15 11 9 5 +36 33 36 37 41 43 46 43 +91 90 86 83 80 77 +48 55 61 63 64 66 65 +55 55 56 56 62 +64 64 59 58 57 56 50 +16 13 11 10 7 5 8 +26 26 31 32 34 41 +58 57 50 48 46 42 +23 19 18 16 17 +15 18 21 24 27 29 32 +63 60 57 55 54 52 51 +84 82 80 77 76 75 +40 42 44 46 48 51 53 +91 89 86 84 81 +37 40 41 42 45 46 49 52 +90 87 85 84 83 81 78 76 +9 10 11 13 14 16 +35 34 31 28 26 24 22 21 +96 95 93 90 87 86 +45 46 49 52 53 55 57 +54 56 58 61 62 +44 47 48 50 53 +79 81 82 83 86 87 +15 17 18 19 22 25 26 27 +26 29 30 33 35 +90 89 87 86 85 83 80 +79 80 82 83 84 +42 45 47 49 50 +89 88 87 86 83 82 80 +22 23 24 26 28 +51 48 46 43 40 37 36 +1 3 5 7 8 10 12 +87 84 83 81 80 78 77 76 +76 74 71 69 66 63 61 +20 21 23 26 29 31 33 34 +48 47 45 42 41 40 39 +56 53 52 51 48 45 42 39 +31 32 34 36 39 41 +40 37 35 33 30 27 25 22 +76 74 73 70 68 66 +67 64 62 60 59 +35 34 31 28 25 22 +93 91 90 88 85 83 81 +7 10 11 14 16 17 20 +37 35 34 31 28 +58 59 60 63 64 66 68 +13 12 11 9 7 5 4 1 +80 77 74 73 71 68 65 63 +93 91 89 88 87 84 81 79 +87 84 81 78 77 75 +70 72 75 78 80 82 83 86 +69 67 65 63 60 59 56 53 +23 20 17 16 15 +64 65 68 70 71 72 +79 80 81 84 86 +15 18 21 23 25 27 +39 36 35 33 30 27 24 22 +54 53 51 50 47 +68 67 65 63 61 59 +99 97 95 93 92 91 88 +71 68 65 63 61 59 +66 64 63 60 58 57 +51 53 56 58 61 63 +84 87 89 91 92 95 +30 31 34 37 38 39 +57 58 59 61 63 66 +62 64 65 68 70 71 72 73 +23 21 20 17 15 14 12 11 +53 56 58 60 62 63 64 +76 74 72 70 68 66 64 61 +88 85 84 83 80 78 +41 38 37 34 33 31 29 +33 35 37 39 42 45 +34 32 29 26 24 +95 93 90 87 84 82 79 78 +21 24 25 28 31 +83 86 88 91 94 96 98 +83 81 79 78 77 74 71 69 +84 83 80 79 76 75 74 +35 32 31 28 26 23 22 20 +48 46 44 41 38 37 +89 91 93 94 97 99 +14 16 17 18 20 +23 22 19 16 13 10 +78 80 81 82 84 85 88 +25 23 22 21 20 +33 32 29 28 26 24 21 19 +80 78 76 73 72 69 +82 81 79 78 76 73 +54 51 48 45 44 +34 32 29 27 24 21 19 16 +70 69 66 63 61 58 55 52 +29 26 24 21 18 15 13 12 +81 84 86 87 88 91 93 94 +85 82 79 76 73 70 68 67 +82 81 80 77 76 75 72 +42 39 38 36 35 +71 74 77 80 81 83 +32 35 36 37 40 +59 57 54 53 52 +36 33 31 30 27 25 23 +89 86 83 81 79 76 74 73 +76 77 80 83 86 88 +72 70 69 66 65 +27 25 24 21 19 16 +57 60 63 66 69 70 73 75 +34 35 36 38 40 43 46 +26 28 29 32 35 37 38 41 +14 12 9 7 5 +11 14 17 18 21 24 +47 45 42 39 36 33 +52 55 58 59 60 63 +83 81 80 78 76 74 71 70 +10 11 13 15 16 17 20 22 +67 66 63 61 58 55 +83 86 89 91 93 +78 81 84 87 89 +51 49 46 45 42 40 38 +69 66 63 62 61 60 58 +69 70 72 75 78 80 83 +26 29 30 33 34 35 +48 45 43 41 38 +76 73 70 69 67 66 +67 70 73 76 78 81 +48 46 43 41 38 36 +77 75 73 70 69 +4 6 9 10 12 14 17 20 +21 23 24 27 29 30 31 +77 75 74 73 70 69 66 +18 17 16 14 12 +46 44 41 39 38 +29 26 25 24 21 19 +5 7 9 10 11 12 14 15 +67 64 63 60 57 55 54 +26 29 30 31 33 36 38 +27 29 30 32 35 +27 25 23 22 20 19 16 14 +51 53 55 58 59 60 +22 19 17 16 15 12 11 +29 26 23 22 19 16 14 12 +5 7 9 10 13 +51 52 55 56 57 58 59 +46 49 50 53 54 57 58 +48 47 46 45 43 42 40 37 +57 59 61 63 66 69 +60 58 55 53 51 48 47 +89 88 86 85 82 80 79 +47 45 42 41 38 36 34 +93 92 89 86 85 83 +46 49 52 54 55 57 58 60 +47 49 51 54 57 58 61 62 +35 38 41 42 45 48 49 +45 47 49 52 55 58 +60 57 55 52 51 49 47 44 +62 61 58 56 55 53 50 47 +49 47 44 43 40 39 38 37 +56 53 50 49 48 46 44 41 +42 41 39 37 34 33 +57 55 52 50 47 45 +86 83 81 78 75 73 70 +13 12 9 8 7 4 +4 5 6 8 11 13 16 +25 23 22 19 17 16 14 +85 88 89 90 91 92 93 96 +89 88 85 84 81 79 78 +37 39 42 43 46 49 50 +78 76 73 71 68 67 66 +44 45 48 49 51 54 57 +61 60 58 56 55 54 52 50 +13 16 19 20 23 24 +50 47 44 43 41 +21 23 24 27 28 31 34 +22 25 28 31 32 +25 26 29 31 33 35 +87 89 91 93 95 98 +16 17 20 21 23 24 +71 72 75 78 80 81 +79 82 84 85 88 +69 66 63 60 59 57 54 53 +42 40 39 37 34 33 30 +62 64 67 68 70 71 74 +22 24 27 30 33 34 35 +13 11 10 8 7 5 +98 97 94 92 91 90 +24 27 30 32 34 36 38 40 +7 10 11 12 14 15 18 +75 76 79 80 83 86 89 90 +17 14 12 11 8 7 +60 61 62 64 65 66 68 +67 68 71 72 74 +50 53 54 55 56 58 +68 65 64 61 60 58 55 54 +85 84 83 82 79 +54 57 58 60 61 +70 71 74 76 78 81 84 86 +28 27 26 23 22 21 +88 90 91 93 96 99 +39 38 37 36 33 31 30 +41 43 44 46 47 +70 68 65 63 60 59 58 55 +61 60 59 57 55 53 52 49 +54 52 51 50 47 45 44 +11 14 15 18 19 20 21 +76 73 70 68 65 64 61 +45 46 49 52 53 54 +51 50 47 44 41 +27 29 30 33 36 38 39 +27 25 22 21 20 +56 57 58 60 63 65 66 67 +8 6 5 3 1 +51 49 46 45 42 39 38 36 +75 73 71 68 65 +77 78 81 82 83 +74 72 69 68 65 63 61 +19 21 24 25 27 +24 26 28 29 32 33 36 39 +57 56 55 52 49 +63 62 59 57 56 53 +44 47 49 51 53 54 55 +26 25 22 19 16 +78 76 73 71 68 66 +74 72 69 68 66 63 60 +32 33 35 36 37 39 40 41 +24 26 28 29 32 35 +65 63 62 61 60 59 56 54 +53 50 48 47 46 43 +61 58 56 55 53 52 51 49 +48 46 45 43 40 38 +15 12 11 8 6 5 4 +31 28 26 23 22 +54 55 56 58 60 62 63 66 +65 67 69 72 74 76 +46 49 52 54 56 +35 34 32 29 26 +12 11 9 6 4 +19 18 16 13 10 9 +35 32 31 30 29 +51 53 55 56 58 60 62 +23 20 19 16 14 11 +14 17 18 19 21 23 +3 4 7 9 10 13 +37 35 32 31 28 26 24 22 +17 20 21 23 24 27 +37 40 42 44 47 48 51 52 +77 76 73 71 70 69 66 +91 90 88 87 86 85 82 80 +71 68 65 62 59 58 57 +6 8 10 11 13 16 17 +28 29 31 34 37 39 41 44 +2 5 8 9 10 13 15 +67 65 63 62 59 56 53 51 +5 6 9 10 13 16 19 21 +84 86 87 90 91 93 95 97 +46 47 49 52 54 +74 72 71 70 69 +8 9 11 14 17 19 20 23 +28 25 22 19 18 16 15 +29 30 32 33 34 36 37 40 +73 74 75 77 79 82 +37 36 35 34 32 30 27 +15 16 17 18 20 +71 70 69 66 65 63 61 +26 24 21 18 15 +66 69 72 75 78 +94 91 90 89 87 86 +46 48 49 51 52 55 58 +27 24 22 19 17 16 13 +65 66 68 71 73 76 79 +59 58 56 54 53 51 +35 36 39 40 41 43 45 +22 25 27 29 31 34 36 39 +33 35 36 37 38 +88 87 86 84 81 79 +43 40 38 35 32 30 27 +34 36 39 40 43 45 47 +27 26 24 23 21 18 15 14 +56 55 54 52 49 48 45 43 +26 27 30 33 36 39 +87 90 92 93 94 97 98 99 +67 66 63 62 60 58 55 +86 84 81 80 77 75 73 +64 61 58 57 55 +64 65 67 70 72 73 +86 89 92 94 96 99 +50 47 46 44 42 39 37 36 +77 80 82 85 87 89 +69 66 65 63 60 +33 36 37 39 41 43 45 46 +73 74 75 78 81 82 +52 49 48 45 42 41 38 35 +23 20 19 18 17 +34 36 37 38 41 44 +52 54 56 57 60 62 64 67 +15 18 19 20 23 25 +13 12 11 8 6 3 +15 16 18 21 24 26 27 +64 66 69 72 73 76 79 80 +40 41 43 45 46 48 50 52 +24 26 29 31 33 34 36 +52 50 48 46 44 41 40 37 +24 21 18 15 12 11 +77 80 81 82 84 85 87 88 +59 56 55 54 53 +51 50 47 45 42 40 +66 69 70 73 75 77 79 80 +43 46 49 50 51 +62 64 67 69 71 73 75 +66 68 71 74 76 78 79 80 +14 12 9 7 4 +26 29 31 33 36 39 +70 71 74 76 77 +82 83 86 89 91 93 95 +55 52 51 49 48 +18 17 16 13 11 +26 28 29 32 35 36 39 +34 37 38 41 43 44 47 48 +61 64 65 68 69 71 73 +84 86 88 90 93 +52 55 56 59 61 64 65 68 +16 18 21 23 26 28 30 32 +9 12 13 16 18 +20 21 23 26 28 29 30 33 +89 88 85 84 83 80 78 76 +71 70 67 65 62 +36 34 32 29 27 25 +86 84 82 81 78 77 +36 39 40 43 45 47 48 +80 77 76 73 72 71 +24 21 20 19 17 14 12 9 +39 37 35 34 33 30 27 +44 47 50 51 53 56 58 61 +66 64 62 59 58 56 53 50 +62 60 58 56 55 52 +67 69 72 75 77 +62 64 66 67 68 69 70 73 +41 43 45 48 51 +40 42 43 45 47 +33 35 37 38 40 41 +17 18 20 23 24 25 28 30 +16 17 19 21 23 +57 59 61 64 66 68 69 72 +99 96 94 93 90 +76 77 80 81 82 83 85 +21 19 17 15 12 11 +65 68 71 73 76 79 +7 10 11 14 17 20 21 +80 79 76 73 70 +76 79 81 83 84 87 89 +82 81 79 76 75 +50 47 45 44 43 42 40 +97 95 93 91 89 86 83 +88 89 91 93 95 96 +13 11 9 7 6 3 2 +74 76 79 82 85 87 90 93 +93 90 88 87 85 +51 49 46 44 43 40 39 +6 7 10 13 16 17 20 +77 75 73 70 69 66 64 63 +30 33 34 35 38 +49 51 52 54 56 +22 24 25 28 30 +18 19 22 24 27 30 33 35 +83 84 87 89 92 +32 35 36 37 40 43 +18 17 16 14 13 10 9 +53 51 49 46 45 42 +64 65 68 70 72 +72 69 68 65 64 +55 53 50 49 46 43 42 39 +53 52 50 48 45 42 39 +71 73 75 78 81 +83 85 86 89 91 94 +84 85 87 89 92 93 +27 24 23 22 19 18 +19 22 24 26 27 +58 57 56 55 52 49 47 +80 79 76 75 74 +89 86 83 80 77 74 +18 20 21 22 25 +30 29 27 26 24 +79 80 81 82 83 86 88 +83 81 78 77 76 73 71 +7 8 11 13 16 +28 27 26 25 22 +27 30 31 32 34 37 +87 86 83 81 80 79 78 75 +19 21 23 26 27 30 32 +38 40 43 45 48 51 52 55 +1 2 3 5 6 8 10 +24 21 19 16 14 +61 59 56 54 52 51 49 48 +39 37 34 32 30 27 24 +41 43 44 45 47 +86 85 84 81 80 79 76 74 +38 41 44 45 46 49 51 +50 53 54 57 58 59 62 65 +88 89 90 91 94 95 98 99 +73 76 78 81 83 85 +34 35 36 39 41 42 +26 29 30 33 34 +49 46 44 41 40 +66 69 70 73 75 76 77 78 +31 34 37 38 39 41 +58 60 63 65 66 68 +75 73 72 71 70 69 66 63 +68 71 73 76 79 81 +50 52 55 56 57 58 61 64 +98 95 94 93 90 +40 37 34 32 30 +73 76 77 80 81 84 85 +65 64 63 61 59 58 +52 53 55 57 58 60 61 63 +66 69 72 73 74 77 +77 76 73 71 68 67 66 63 +20 17 15 14 12 10 8 +69 67 65 63 62 61 59 56 +89 86 84 82 80 +46 47 48 51 53 54 56 +72 69 68 65 64 61 59 56 +33 35 37 40 43 46 48 +62 60 58 56 55 54 53 50 +99 97 96 93 92 90 88 +39 42 45 47 48 51 +74 75 76 79 81 82 83 86 +64 66 67 69 72 +38 35 34 33 30 +19 16 14 11 9 7 6 5 +66 65 62 60 57 55 +34 32 31 30 27 +76 75 74 73 71 +29 30 33 34 36 38 +33 36 39 41 44 +25 26 28 29 31 33 36 +13 11 8 7 6 4 +94 92 89 86 84 82 +54 51 50 49 46 44 43 41 +2 3 5 8 11 12 15 +30 31 34 36 38 +20 23 26 29 30 32 35 36 +61 64 65 68 71 +51 54 57 60 63 +21 22 23 26 27 30 33 35 +17 20 22 25 28 29 +44 42 39 36 34 31 30 29 +27 24 21 20 19 17 +32 30 29 28 26 24 21 19 +44 45 48 50 53 54 56 +89 86 83 81 80 78 75 72 +70 67 64 63 62 60 +52 53 54 57 59 +27 29 32 33 36 39 40 42 +32 34 35 36 39 42 45 48 +61 64 66 68 71 72 74 +33 30 28 27 24 23 20 +31 29 27 24 21 19 +21 20 19 17 15 12 +42 44 47 50 53 55 56 +77 79 80 83 86 87 88 91 +37 36 33 31 30 28 27 24 +13 16 19 22 25 26 29 +35 34 33 32 29 27 25 +50 52 53 56 57 59 +77 74 72 70 69 68 65 +88 91 92 93 96 +76 78 81 82 83 +64 66 68 69 70 73 74 75 +68 67 66 65 62 60 58 +57 56 55 52 50 48 46 +61 59 58 56 53 +15 12 9 6 5 3 +70 68 66 64 61 60 57 54 +75 78 81 83 84 85 87 +24 26 27 29 30 31 34 36 +53 56 59 60 63 66 +1 3 5 7 8 9 +89 87 84 82 79 78 76 75 +30 27 25 22 21 +37 36 34 31 30 29 +19 18 15 12 11 8 7 6 +60 58 55 54 52 50 48 +7 8 9 12 15 17 +89 90 93 94 96 +27 24 23 22 21 19 17 16 +22 20 19 18 15 14 12 +85 86 88 90 92 95 96 +38 39 40 42 45 +24 25 28 31 33 34 +70 69 68 67 65 63 62 +15 13 12 9 7 4 2 +49 48 45 42 41 +57 60 62 63 66 68 +31 34 37 38 40 +27 28 29 30 33 36 +36 34 31 30 29 28 27 +53 51 49 46 43 +95 92 90 88 85 83 +74 73 72 70 67 64 +23 20 17 16 14 13 +95 92 89 87 85 82 81 79 +36 35 34 32 31 30 29 +50 52 54 57 60 63 64 +58 61 62 63 64 65 68 70 +39 40 42 43 45 48 51 +85 84 81 79 77 74 +45 46 47 49 52 +15 16 17 20 21 22 +86 83 80 78 76 73 70 68 +51 54 57 59 62 +71 73 76 77 79 80 +68 69 72 74 77 78 79 81 +13 11 8 6 5 3 +50 52 53 56 57 59 60 +60 62 64 66 69 +80 83 84 86 88 +8 10 11 14 16 +43 46 47 50 53 56 +55 54 51 50 48 45 43 +66 68 71 74 75 +84 86 87 88 90 93 +38 37 36 33 30 27 26 +29 31 34 37 39 +12 10 9 6 4 +72 69 67 65 63 62 +23 25 26 29 30 32 +70 71 74 77 80 81 84 87 diff --git a/tests/aoc/2024/day03.k b/tests/aoc/2024/day03.k new file mode 100644 index 0000000..99b8ead --- /dev/null +++ b/tests/aoc/2024/day03.k @@ -0,0 +1,34 @@ +i:,/0:"day03.txt" +m:{i[x + !12]}'&"m"=i /possible matches +m:m[&{&/"mul("=4#x}'m] /valid starts +m:m[&~^{x?")"}'m] /valid ends +m:{((4_x)?")")#(4_x)}'m /within parens +m:m[&{~^&/"0123456789,"?x}'m] /valid inner ++/{*/.'","\x}'m /part1 = 173419328 + +/part2 +/find valid mul(x,y) indices +/filter for only indices within validrange blocks + +doi:0,{d@&(&/"do()"=)'x@((!4)+)'d:&"d"=x} i /do indices +donti:{d@&(&/"don't()"=)'x@((!7)+)'d:&"d"=x} i /dont indices +vr:+{x,donti@*&donti>x}'doi /valid ranges + +vs:{&/"mul("=4#x} /valid start +ve:{~^x?")"} /valid end +vi:{~^&/"0123456789,"?4_(x?")")#x} /valid inner +v:{vs[x]&ve[x]&vi[x]} /valid mul(x,y) instruction + +mi:&"m"=i /m indices +mi:mi[&v'{i[x+!12]}'mi] /valid m indices + +/WRONG mi:mi@&{x<(vr@1)@*&x<*vr}'mi /valid range m indices + +/each mi is valid if: +/ doi to it's left is greater than donti to it's left +mi:mi@&>/(+{doi@*|&doi*mul(343,826)@}where()mul(211,118)select()why()%who()()do()'where()'+[!--<:mul(734,544)@when()who(239,383)+!,^~mul(362,147))mul(448,470)}where(916,77);select()/mul(885,970)what()(mul(926,912)when(),,-;?'from()@mul(166,868):from()mul(481,818)[how()who()~!,when()why();mul(2,227)do()![-[ where() ;mul(352,331)-[:mul(724,613)&-~@mul(133,235);when(849,671))where()?~where()@%mul(504,704)why()]<&mul(150,546)^:why();where(793,121)~!')[mul(345,200))[%select()why()^:%where()mul(112,504)'(from(194,838) when()]'why(231,15))$mul(480,487)where(335,714)$(;@{mul(542,838)-(mul(111,392))select()>when()mul(416,295)select()why(){from()<:mul(190,460)$(?how()when()mul(597,500)mul(714,782);?%>!mul(303,578)from(481,153)*&;,mul(373,860)~who()how(){mul(871,673)#@why():/[who():}don't()$<+#-(how()%-do()!when()!when()from(342,582)@mul(877,442)from()mul(874,842);[?how()when()who()from()?(mul(415,601)#+/]:mul(132,522)#where(222,639)]from()&[how()~mul(278,563)?,~-?,(why()>mul(740,126)]select()';>]*,where(),mul(853,590)where()'~+mul(284,920)from()/+};-+[?^mul(656,345)]why()mul(185,132)-mul(770,693)~[what()<@-from()mul(200,187)mul(624,299)}mul(811,930)}+$]${&#?mul(805,62){;?when()/,:what()('mul(602,308)#}&^select()<@@how()why()mul(572,706)mul(327,202){'^*who()@mul(965,541)why()?/{[&when()mul(298,736):@}} mul(667,224)why()(- $#mul(641,534)'^mul(981,964)?;}don't()from(208,511)]})from(823,301)why()select()mul(462,251)select(){from()why()$how()'}}mul(83,963)how()$}+~:!,)mul(317,238)*'{ do()mul(408,63)-,*where()who()*@mul(761,933)who()who()&]^mul(757,153) mul(761,877):@#*^where(790,345)$why()mul(464,363)mul(546,662)from()do()-when())'^mul(133,153) +$when()*+:@{[who()do():#when(755,316)select()<){+@mul(228,645)$/who()[+$>from()mul(379,971)what()( )where()-mul(333,963)/(@$why()#when()why()!select()do()[!when()^why()#mul(254,172)!!mul(150-/:!%:who()select()what()?mul(511,672)]?'do();/how()when()&select()select()mul(602,681){where() }mul(52,967)mul(373,270)'&^from(767,468)#@who();-mul(965,683)how()#]#$(@mul(205,224)&*^*!why():&$mul(464,992)who()(&don't()&^mul(41,520)]mul(639,416)select(),who()$mul(322,578)&%%where()(how(580,932)what()&mul(529,241)[??#mul(758,621)*mul(771,275)>select()(why()]mul(87,225)how()how()~@what()how()where()mul(335,347),mul(585,232)$select()/&}^;( mul(594,43)who()~@mul(380,624)mul(678,429);what()what() -/mul(660-select()/mul(305,408);select()~mul(815,711)mul(757,285){how()/why()select()@@){^mul(902,868)from()/'when(){@;mul(295,421)-from()!}<:mul(308,315),when()%/>^$mul(542,950)do()@&^#?~!'how(525,85)mul(191,12)?%]+mul(787,915)-#{#[where(532,178):mul@-mul(941,234){where()?$how()^;%*[$[@[mul(92,115)don't()how()mul(493,708)>:;)~when()&mul(710,155)('<^$+$mul(166,985)]{+mul(171,503)%{select(6,86)-'select()mul(825,487)when()/[:?<>mul(459,298)>]select()!don't()'{:;mul(96,934)}-*,%,don't(){+who()$who()!who()mul(517,278)~}@mul(574,84)^&%:what(),mul(723,445)?$mul(952,833))? @select()~who();*]mul(866,509)}who(174,511)]mul(369,695)-what()select(){~>{mul(191,238)&:!:>'what(),mul(381,727^*:where(670,586))-:mul(922,333)how()?how()&?,;when()]@mul(432,354)}^mul(263,98);}]:,mul(661,118)%! [from())mul(276,921)^]]]^when();select()mul(895,325)why()+where()!from(294,522)^^^~)mul(153,481)mul(861,802)[]how()(*&mul(219,815)-:&%what()what()@mul(377,33)mul(156&<}how()^mul(654,99)/?>-~select()@)mul(20,758)mul(378,510)(^)*who()?#<+mul(652,169)%when():*+how()mul(860,302+don't()'!who()/) mul(376,810)<: !why()>{mul(431,544)+mul(860,291)when()from(),-why()#mul(367who() <]>){from() @,mul(194,2))$why()how()*when()mul(965,720)@]#){where()[,*+mul(461,205)^how();;(^)>mul(10,206)^,< who()%$%~mul(716,424)[mul(76,949)mul(605,201)-mul(84,478)%when()@+~what()~mul(562,278)mul(746,153)who(987,176)mul(70,785)mul(583,929)-why()/why()%! +mul(512,816)'who()mul(869,983)?mul(735;*why(521,585)$do()from()(how()select()mul(594,821++@!mul(602,847)mul(561,601)mul(351,649)~!why()+][?&mul(884,49)}!when()&#where()when()))+mul(853,585)~$$@mul(462,772):-)what()mul(871,925)$>*^mul(488,569)do()what():when()/(+<+<^mul(556,667)select()where()mul(308,658)@/when()]where()({do()%mul(281,279))'$]do()$#from()]::;*;mul(831,254)who(33,9)mul(593,778)/)$!{how() mul(716,94)where()#+where()~+~[select()?do()+<,mul(119,848)how()(what();'[,mul(205,435)mul(462,597)#(<:mul+mul(821,403)&**mul(309,390) +from()*/)where()*@>mul(425,149)[/:,(,;:{#mul(442,406)>(;<$,&%*mul(954{why(467,228)select();{>)%how(604,161)what()from()mul(359,89),from()*];~when()]mul(403,60)?[];*+#,mul(528,565#>;*- mul(894,804) don't()-mul(736,961)mul(169,583)where()#%~@mul(943,85)$,mul(820,12)]mul(86,786)~mul(453,237how()]what()^when()!-mul(826,572)from()?*select())]from())mul(3,29)how()who()@/+when();%mul(285,796)when())mul(193 ^@,from()#what()>'$(mul(313,971)<#mul(871,501)@how();from()from()mul(648,120)$^}{how()&)(mul(170,653)^who()mul(284,948)*mul(338,643how()~who()!}@(>mul(822,278)when(990,725)+do(),what() mul(7,493)][when()why(530,953)mul(241,105)mul(280,649)when()'??~mul(995,913)&how()select()$~mul(461,746)[:;{]//-mul(419,572)mul(347,770)how()mul(566,424)mul(180,109);select()when()where(827,268)/<,+mul(629,648);'(~mul(46,371)+*$what()mul(203,987)mul(686,836)(when()from()what()}];]mul(716,962)where()mul(821,591):>^mul(902,217)$;>where()?~}select()mul(702,261)when(){~!!mul(181,296)?#mul(698,522)@mul(461,392)~who()(]<%!from()don't()>}),%where()mul(562,168)#;from() where()mul(237,53) mul(268,711)when()~]+{'~mul(36,808)#;[:)select()[when()(who()mul(763,177)from()select()why(){/(mul(701,861)>how()!,*what(753,574)@mul(238,46)mul(663,748)&mul(742,855)##/)-mul(818,603)>what()-,mul(835,282)select()>;#*mul(897,85)/where(){!mul(852,153)mul(633,603)-what():where()[[mul(776,51{&from()>%>^mul(64,533)mul(992,697)'when(),mul(331%#%$]*;who()mul(799,100))@[;%<%how()when():mul(781,214)}%!what()from()how()from()mul(609,497)-how()&who()mul>[when()#,[select()mul(848,464)mul(998,874)$]mul(933,654)#*>what()select():[;#when()mul(288,108)select() mul(856,529))#&from()[do()where():who()*[%]?mul(236,606)$*)when());^}mul(458,967)from()who()from(){mul(379,719)<*mul&#!/usr/bin/perl>!who()#mul(357,342)?how():/[from();mul(419,654) +![/mul(665,722)mul(44,793)from() $when()[/from():?mul(910,529)!how()!@>where()mul(85,692)]-%/mul(884,81)who(521,804)what()what(){what()when()mul(990,766)mul(793,200)select()mul(755,282),[(where()+;(#mul(394,96)mul(351,116)-^(mul(641,709)$,mul(982,628!,,[when()^~(how(184,286)what() mul(750,635)}%{%<> ?&mul(931,9)$[mul(698,488)~how():why()^*mul(564,153)}-where()why() what()mul(714,569)from()?[?mul(80,973):#'+don't()}{how()}mul(437,592)(where()?,when()how()%:#mul(295,199) +)}from()>what()}mul(877,282)'where() &~?what(){*mul(222,408)mul(827,819)why() # do();;,why();,[mul(321,402)}who()'mul(755,90)select()what())mul(977,344)#why()who()?+-mul(383,635)($;when()#mul(900,601)+do();mul(206,834)mul(383,432){what(360,811)mul(827,802),)?$where()who()?{do()when()?$why();{{mul(544,551):mul(380,98)where()mul(231,896)mul(248,602)select()(who()mul(338,121),why()):>mul(95,711)?'{mul(638 -)@!mul(681,304)what()~^%(mul(116,692)}&select()why(664,706)why();}&mul(103,869), mul(205,12)}-[mul(979,353)when(){mul(303,476)-/<$!%%]mul(358,341)select()&}who()why(163,835);>+mul(34,734)mul(330,527)mul(428,357)who()[mul(680,857)>-/;when() what()/ select()mul(218,336)&-who()when()where()who()-from()+mul(84,105)when()&?select(477,434)($#'mul(128,545){+'why()+),?'$mul(745,121),what())mul(133,419)@$/$+;who(115,246)^/(mul(135,230)~:^mul(675,10)$(what()%do()+what()-why()mul(838,662)how()%mul(683,258+*mul(492,582))mul(246,947){(mul(548,714)from()*~+[-why()select()mul(118,285)where(){<}/%mul(221,266)#mul(79,261)%why()mul(349,642)what(){<(mul(811,491)mul(852,262)what()where(752,95)<^)mul(133,969{select()mul(252,913)why(765,304);?what()mul(661,7):#%?!'mul(744,62)%)mul(532]select()}when()when()]'[%^mul(565,470)^why()mul(9,544),@ what()mul+&!where()*+mul(659,140)mul(982,671))*@% *''mul(508,215))from()-!]> ~mul(413,418)>[(^,,mul(512,738)!-@what(){!<&mul(545,683)mul(461,493)~'mul(255,513+}{~from()what()mul(10,277)*who()&why();!&+-mul(281,940)how()>/$mul(246,406)@{~how()/<>!don't()-:~(mul(741,150)}mul(271,458))when():!&?mul(645,221) why()))mul(943,83)(select()@'@mul(557,649)(select(93,936)when()~?what() mul(196,320);where()?>who()mul(208,759)+what()&%@why()$do()mul(931,359)#?who()# [*##mul(754,721)@]#from()>;,mul(924,810)~]mul(504,18)&when()+!'>(mul(420,227)<[{{why()do()$who(){','#what()mul(11,549)mul(275,839)+**&what()mul(960,111)~mul(863,559)mul(997,395)?(}>where(),mul(553,109)[^ 'mul(337,751)<>@++;when()what(278,714)mul(50,770) +when()@select()where() ([mul(747,2)~ do()]'[why()(>why()mul(20,45)* ,^&!'~mul(769how()from()-/;mul(101,318)from()what():from()who()%mul(114,822)*why()[>select()where(832,207)from(214,70)from()mul(93,917),*$}:> ~what()mul(650,282){how()mul(556,140)where()what()mul(876,167)+&mul#who()~#}@mul(902,167)^!^%mul(137,823)>mul(550,698)how()'$mul(843,697){)'#-@ )/-mul(617,639)from()select()mul(838,59)why():from()when()mul(512,68) when()from()from(); don't()mul(292,873)){+don't(){]mul(663,183)+select();/- how()+ *mul(638,19)]<}:!&where()%mul(321${mul(145,779)what()[~/>:don't()*##why()*+'mul(268(]$*+how()-'when()mul(845,280)what()^}@mul(989,77)'select()*$?$mul(368,599)@@:'@where())mul(348,748) }$;%+select()?%mul(70,929),,^[]^select()#mul(830,51,?&!mul(858,374),select()when()}why()%what()]select()?mul(180,625)!mul(73,99)mul(445,334)/,!@@mul(464,542)!,from()$?[mul(516,277)]mul(617,538)mul(292,731)-*$*what(289,332):why()>mul(994!select(),>$)mul(289,272){why()/:^/mul(983,85)when()mul(18,611)}mul(466,826)who()--mul(470,873)!+*&mul(101,733)'where()mul(983,778)mul(974,217)/-+/]@from(),>mul(483,292)'?$<},*what()mul(599,699)}/from()[*where(){mul(466,649)#'*:!where()mul(434,181)-/what()$/select()}mul(135,403)+'mul(988,367)mul(699,753)^,':+}})do()mul(876,320)why(){~/mul(550,960)$(*:-?$what()mul(259,261)mul(140,435)mul(981,455)#&}+'mul(434,245))!{mul(905,337)@select(716,497)(#}how(880,682)*from()mul(408,109)!}%don't()%<)how()%/mul(485,635)&from(), :;%/mul(421,957)#mul(138,916)mul(779,979)who();mul(694,921)/[/$!&@select()mul(599,341),}/}#'where()what()who()>mul(990;@ !-why()don't(){*:!what(): ^mul(734,796)-:/where() mul(940,41)what(609,666)'how()what(522,700)mul(602,249)^where()(!~)mul(880,415)select())>(%}mulwhen()why()?<+?mul(744,997)from()#when()+{~where()mul(37,232)#mul(996,205)mul(993,863)why()mul(144,204),,what()mul(169,251) +%who()^why() mul(410,625)from();what(){[+who(67,306)]$mul{how()mul(456,501);&from()]%<&don't()(!where()}@>}!,mul(566,94)> )when()<-mul(725who(77,893),@>who()-!when()mul(197,527)what()+?+, mul(170,190)@*when(){@why()don't():when()^how()>{}]mul(660,36)when()],]how()@~who(14,397)]mul(260,88)who()~select()when()<{,-do():>why() mul(276,661)?)~how()/'&why()mul(199,824)&,$*'(#) mul(919,245)&who()/mul(469,339)[[>why() do()mul(388,947)who()#~mul(145,154>,~{ mul(752,410){,!~why()why()#mul(498,700))!; mul(290,290):@from(176,870)[%@mul(340,622)(when(821,843)+when()%why()+mul(663,681)@^/(? '{select()%don't()where(51,291)&]:mul(253,129)why(){where()select(),^mul(795,485);!why():@mul(799,33)//mul(97,531)from();?@%%mul(833,760)!&!/^#&mul(867,677))@mul(483,613))how()@;)^%;?mul(518,57)!$)-why()what()}how(){*mul(919when()$-),mul(206,121)}+']){mul(46,767)-mul(32,66)where()mul(986,477)why()}:;select()@ {{mul(93,728)%;(%where()select()mul(955,534)($~;mul(743,671)[+<%,+$$$mul(154,834)'who()*select()}#when()!mul(129,508)why()who()++when()mul(343,945)^what()^) >do():mul(586,351):%##who()>#?mul(498,937)],mul(326,512)from()!%#>}from()mul(610,769)how()/mul(955,551)#%,]mul(376,333)<-;when(435,299)#why(){>*%mul(609,536)!>{> :+%&mul(304,490)<[mul(690,195) //where()#&;~mul(499,478)! what()why()why()^mul(923,423)&when()mul(96,306)^why()who()%from()]/how()]mul(731,186)///mul(516,127)$-{ why()!]'mul(280,921)::@(]{how() what()mul(198,835)[-!from()*%mul(149,825) where()where()mul(660,669)(]&@mul(803,556)how(677,591)mul(609,507))?/>@'mul(176,185)[when()from()$}when()where()mul(496,706)why()^<&~?{/%)mul(349,473)how()>?{]!mul(242,815)@%-?;[{~mul(56,909) mul(638,125)#>^select()>^-?-'mul(517,633)why()]%mul(480,954)?'-'>}mul(5,623)what()}from(904,521)](who() Result { + let mut env = Env { names: HashMap::new(), parent: None }; + + // let r = eval(&mut env, scan(s).unwrap()).unwrap().unwrap_noun(); + match eval(&mut env, scan(s).unwrap()) { + Ok(r) => { + // let r = r.unwrap_noun(); + // println!("k_eval({}) = {}", s, r); + Ok(r) + } + Err(e) => { + println!("k_eval({}) Err: {}", s, e); + Err(e) + } + } +} +// fn k_evals(s: &str) -> String { format!("{}", k_eval(s)) } + +#[test] +fn test_ngnk_tests() { + let mut file = match File::open("ngn/k/t/t.k") { + Ok(file) => file, + // Err(_) => panic!("no such file"), + Err(_) => { + println!("Skipping test_ngnk_tests() t.k does not exist. Run bench-vs-ngn-k.sh first."); + return; + } + }; + let mut file_contents = String::new(); + file.read_to_string(&mut file_contents).ok().expect("failed to read!"); + let lines: Vec = file_contents.split("\n").map(|s: &str| s.to_string()).collect(); + + assert!(lines.len() > 0); + let mut test_count = 0; + let mut failed_tests = 0; + + // TODO add support for these lines + let skiplines = [9, 10, 14, 15, 28, 30, 31, 32, 34]; + + for (i, l) in lines.iter().enumerate() { + if skiplines.contains(&i) { + test_count += 1; + failed_tests += 1; + println!("\nskipping line {} known failure: {}", i + 1, l); + } else { + println!("\nline {}: {}", i + 1, l); + let t: Vec<&str> = l.split(" / ").collect(); + if t.len() != 2 { + println!("Skipping dud line: {}", l); + } else { + test_count += 1; + // assert_eq!(k_eval(t[0]), k_eval(t[1])); + let res = k_eval(t[0]); + if res != k_eval(t[1]) { + failed_tests += 1; + println!("Failed test: ({failed_tests}/{test_count}): {}", l); + match res { + Ok(k) => println!("{}", k), + Err(e) => println!("{:?}", res), + } + } + } + } + } + println!("test_count: {}\nfailed_tests: {}", test_count, failed_tests); + assert!(failed_tests == 0); +} diff --git a/tests/tests.rs b/tests/tests.rs index 06efec6..fa69b1d 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -6,6 +6,15 @@ use roklang::*; use roklang::KW::*; +fn k_eval(s: &str) -> K { + let mut env = Env { names: HashMap::new(), parent: None }; + + let r = eval(&mut env, scan(s).unwrap()).unwrap().unwrap_noun(); + println!("k_eval({}) = {}", s, r); + r +} +fn k_evals(s: &str) -> String { format!("{}", k_eval(s)) } + #[test] fn test_scan() { assert_eq!(scan("1").unwrap(), vec![Noun(K::Bool(1u8))]); @@ -663,6 +672,10 @@ fn test_table() { println!("{:?}", s1); println!("{:?}", s2); assert_eq!(s1, s2); + + assert_eq!(k_eval("!+`a`b!(1 2 3;2 3 4)"), k_eval("`a`b")); + + assert_eq!(k_eval("+`a`b!(1;2 3 4)"), k_eval("+`a`b!(1 1 1;2 3 4)")); } #[test] @@ -858,6 +871,16 @@ fn test_scan_fixedpoint() { ); } +#[test] +fn test_v_d_scan() { + let mut env = Env { names: HashMap::new(), parent: None }; + + assert_eq!( + eval(&mut env, scan("3 +\\ 1 2 3").unwrap()).unwrap(), + eval(&mut env, scan("4 6 9").unwrap()).unwrap(), + ); +} + #[test] fn test_parse_functions() { // See https://estradajke.github.io/k9-simples/k9/User-Functions.html @@ -873,6 +896,44 @@ fn test_parse_functions() { }; let mut env = Env { names: HashMap::new(), parent: None }; assert_eq!(eval(&mut env, scan("{2 * x}").unwrap()).unwrap(), f); + + let f = KW::Function { + body: vec![KW::Exprs(vec![ + vec![ + KW::Noun(K::Name("a".to_string())), + KW::Verb { name: ":".to_string() }, + KW::Noun(K::Int(Some(2))), + ], + vec![ + KW::Noun(K::Name("x".to_string())), + KW::Verb { name: "*".to_string() }, + KW::Noun(K::Name("a".to_string())), + ], + ])], + args: vec!["x".to_string()], + adverb: None, + }; + let mut env = Env { names: HashMap::new(), parent: None }; + assert_eq!(eval(&mut env, scan("{a:2;x*a}").unwrap()).unwrap(), f); + + let f = KW::Function { + body: vec![KW::Exprs(vec![ + vec![ + KW::Noun(K::Name("a".to_string())), + KW::Verb { name: ":".to_string() }, + KW::Noun(K::Int(Some(2))), + ], + vec![KW::Exprs(vec![vec![ + KW::Noun(K::Name("x".to_string())), + KW::Verb { name: "*".to_string() }, + KW::Noun(K::Name("a".to_string())), + ]])], + ])], + args: vec!["x".to_string()], + adverb: None, + }; + let mut env = Env { names: HashMap::new(), parent: None }; + assert_eq!(eval(&mut env, scan("{a:2;[x*a]}").unwrap()).unwrap(), f); } #[test] @@ -891,6 +952,14 @@ fn test_functions() { ); } +#[test] +fn test_function_local_vars() { + // See https://estradajke.github.io/k9-simples/k9/User-Functions.html + // + let mut env = Env { names: HashMap::new(), parent: None }; + assert_eq!(eval(&mut env, scan("{a:2; a * x} 2").unwrap()).unwrap(), Noun(K::Int(Some(4)))); +} + #[test] fn test_expr_funcargs() { let mut env = Env { names: HashMap::new(), parent: None }; @@ -902,7 +971,7 @@ fn test_expr_funcargs() { assert_eq!(eval(&mut env, scan("f[2;2;2]").unwrap()).unwrap(), Noun(K::Int(Some(6)))); // let mut env = Env { names: HashMap::new(), parent: None }; - // AA TODO a space between verb and arg list changes from verb[args] to verb (eval args) => verb(curry)list + // TODO a space between verb and arg list changes from verb[args] to verb (eval args) => verb(curry)list // assert_eq!(eval(&mut env, scan("+ [2;2]").unwrap()).unwrap(), CurryVerb("+", Noun(K::Int(Some(2))))); } @@ -1110,6 +1179,22 @@ fn test_array_indexing() { let res = eval(&mut env, scan("(1 2 3; 3.14; `a) @ 0").unwrap()).unwrap(); assert_eq!(res, Noun(K::IntArray(arr!([1, 2, 3i64])))); + + // (1;2;3 4)@0 1 + // (1;2;3 4)@2 + let res = eval(&mut env, scan("(1;2;3 4)@ 0").unwrap()).unwrap(); + assert_eq!(res, Noun(K::Bool(1))); + + let res = eval(&mut env, scan("(1;2;3 4)@ 0 1").unwrap()).unwrap(); + assert_eq!(res, Noun(K::IntArray(arr!([1, 2i64])))); + + let res = eval(&mut env, scan("(1;2;3 4)@ 2").unwrap()).unwrap(); + assert_eq!(res, Noun(K::IntArray(arr!([3, 4i64])))); + + assert_eq!( + eval(&mut env, scan("(1;2;3 4)@ 42 42 42").unwrap()).unwrap(), + eval(&mut env, scan("0N 0N 0N").unwrap()).unwrap() + ); } #[test] @@ -1252,7 +1337,6 @@ fn test_rand() { } } -#[ignore] #[test] fn test_find() { let mut env = Env { names: HashMap::new(), parent: None }; @@ -1278,6 +1362,75 @@ fn test_each() { assert_eq!(res1, res2); } +#[test] +fn test_eachright() { + let mut env = Env { names: HashMap::new(), parent: None }; + + let res1 = eval(&mut env, scan("a=/:a:!3").unwrap()).unwrap(); + let res2 = eval(&mut env, scan("(1 0 0;0 1 0;0 0 1)").unwrap()).unwrap(); + println!("res1: {:?}", res1); + assert_eq!(res1, res2); +} + +#[ignore] +#[test] +fn test_eachright_defun() { + // TODO: parse adverbs properly + let mut env = Env { names: HashMap::new(), parent: None }; + + let res1 = eval(&mut env, scan("a{x=y}/:a:!3").unwrap()).unwrap(); + let res2 = eval(&mut env, scan("(1 0 0;0 1 0;0 0 1)").unwrap()).unwrap(); + println!("res1: {:?}", res1); + assert_eq!(res1, res2); +} + +#[test] +fn test_eachleft() { + let mut env = Env { names: HashMap::new(), parent: None }; + + let res1 = eval(&mut env, scan("3 4 5 +\\:(2 3;4 5;6)").unwrap()).unwrap(); + let res2 = eval(&mut env, scan("((5 6;7 8;9);(6 7;8 9;10);(7 8;9 10;11))").unwrap()).unwrap(); + println!("res1: {:?}", res1); + assert_eq!(res1, res2); +} + +#[test] +fn test_eachprior() { + let mut env = Env { names: HashMap::new(), parent: None }; + + let res1 = eval(&mut env, scan("-':1 6 2 3 4").unwrap()).unwrap(); + let res2 = eval(&mut env, scan("1 5 -4 1 1").unwrap()).unwrap(); + println!("res1: {:?}", res1); + assert_eq!(res1, res2); +} + +#[ignore] +#[test] +fn test_eachprior_d() { + let mut env = Env { names: HashMap::new(), parent: None }; + + let res1 = eval(&mut env, scan("5-':1 6 2 3 4").unwrap()).unwrap(); + let res2 = eval(&mut env, scan("-4 5 -4 1 1").unwrap()).unwrap(); + println!("res1: {:?}", res1); + assert_eq!(res1, res2); +} + +#[ignore] +#[test] +fn test_windows() { + let mut env = Env { names: HashMap::new(), parent: None }; + + let res1 = eval(&mut env, scan("3':1 2 3 4 5 6").unwrap()).unwrap(); + let res2 = eval(&mut env, scan("(1 2 3;2 3 4;3 4 5;4 5 6)").unwrap()).unwrap(); + println!("res1: {:?}", res1); + assert_eq!(res1, res2); + + let res1 = eval(&mut env, scan("3 +/':1 2 3 4 5 6").unwrap()).unwrap(); + let res2 = eval(&mut env, scan("6 9 12 15").unwrap()).unwrap(); + println!("res1: {:?}", res1); + assert_eq!(res1, res2); +} + #[test] fn test_max() { let mut env = Env { names: HashMap::new(), parent: None }; @@ -1351,3 +1504,163 @@ fn test_promote_nouns() { assert_eq!(promote_nouns(l.clone(), r), (l, K::FloatArray(arr!([1.0, 1.0, 1.0f64])))); } +#[ignore] +#[test] +fn test_split_strings() { + let mut env = Env { names: HashMap::new(), parent: None }; + + //TODO fix parse error of 2 or more adverb chain + let res = eval(&mut env, scan(r#"","\'("1,2";"3,4")"#).unwrap()).unwrap().unwrap_noun(); + + assert_eq!( + res, + K::List(vec![ + K::List(vec![K::Char('1'), K::Char('2')]), + K::List(vec![K::Char('3'), K::Char('4')]) + ]) + ); +} + +#[test] +fn test_eval_verb() { + let mut env = Env { names: HashMap::new(), parent: None }; + + let res = eval(&mut env, scan(r#"."42""#).unwrap()).unwrap().unwrap_noun(); + + assert_eq!(res, K::Int(Some(42))); +} + +#[test] +fn test_concat() { + let mut env = Env { names: HashMap::new(), parent: None }; + + let res = eval(&mut env, scan("2,3").unwrap()).unwrap().unwrap_noun(); + assert_eq!(res, K::IntArray(arr!([2, 3i64]))); + + let res = eval(&mut env, scan("1 2,3 4.0").unwrap()).unwrap().unwrap_noun(); + assert_eq!(res, K::FloatArray(arr!([1., 2., 3., 4.0f64]))); +} + +#[test] +fn test_grade() { + let mut env = Env { names: HashMap::new(), parent: None }; + + let res = eval(&mut env, scan("< 3 2 1").unwrap()).unwrap().unwrap_noun(); + assert_eq!(res, K::IntArray(arr!([2, 1, 0i64]))); + + let res = eval(&mut env, scan("< 3.0 2.5 1").unwrap()).unwrap().unwrap_noun(); + assert_eq!(res, K::IntArray(arr!([2, 1, 0i64]))); + + let res = eval(&mut env, scan("> 3 2 1").unwrap()).unwrap().unwrap_noun(); + assert_eq!(res, K::IntArray(arr!([0i64, 1, 2]))); +} + +#[test] +fn test_index_take_drop_bounds() { + assert_eq!(k_eval("1 _ 1 2 3"), k_eval("2 3")); + + assert_eq!(k_eval("1 2 3 @ 42"), k_eval("0N")); + + assert_eq!(k_eval("1 2 3 @ 0 1 42"), k_eval("1 2 0N")); + + assert_eq!(k_eval("0 # 1 2 3"), k_eval("!0")); + + assert_eq!(k_eval("0N # 1 2 3"), k_eval("1 2 3")); +} + +#[test] +fn test_group() { + assert_eq!(k_eval("= 1 1 2 2 3 3 4"), k_eval("`1`2`3`4!(0 1;2 3;4 5;(,6))")); + assert_eq!(k_eval("= 1.0 1 2 2 3 3 4"), k_eval("`1.0`2.0`3.0`4.0!(0 1;2 3;4 5;(,6))")); + + assert_eq!(k_eval("= \"foo\""), k_eval("`f`o!((,0);1 2)")); + + // TODO + // assert_eq!(k_eval("= (0 1;1 0;0 1)"), k_eval("!/+((0 1;0 2);(1 0;,1))")); + assert_eq!(k_eval("= (0 1;1 0;0 1)"), k_eval("`01b`10b!(0 2;(,1))")); +} + +#[test] +fn test_imat() { + assert_eq!(k_eval("=3"), k_eval("(1 0 0;0 1 0;0 0 1)")); +} + +#[test] +fn test_reverse() { + assert_eq!(k_eval("|1"), k_eval("1")); + assert_eq!(k_eval("|2"), k_eval("2")); + assert_eq!(k_eval("|1 2 3"), k_eval("3 2 1")); + assert_eq!(k_eval("|1 2 3.0"), k_eval("3.0 2 1")); + assert_eq!(k_eval("|\"abc\""), k_eval("\"cba\"")); + assert_eq!(k_evals("|`a`b`c"), k_evals("`c`b`a")); + assert_eq!(k_eval("|`a`b`c!(1;2;3)"), k_eval("`c`b`a!(3;2;1)")); + assert_eq!(k_eval("|+`a`b`c!(1 2 3;4 5 6;\"abc\")"), k_eval("+`a`b`c!(3 2 1;6 5 4;\"cba\")")); + assert_eq!(k_eval("|(`a;1;\"b\")"), k_eval("(\"b\";1;`a)")); +} + +#[test] +fn test_comparisons() { + println!("test_comparisons() numbers"); + assert_eq!(k_eval("1<2"), k_eval("1")); + assert_eq!(k_eval("1<1 2 3"), k_eval("0 1 1")); + assert_eq!(k_eval("1.0<2"), k_eval("1")); + assert_eq!(k_eval("0N<1"), k_eval("1")); + assert_eq!(k_eval("0n<1"), k_eval("1")); + assert_eq!(k_eval("0n 0n<1"), k_eval("1 1")); + assert_eq!(k_eval("0N<1 1 1"), k_eval("1 1 1")); + assert_eq!(k_eval("0N<1 0N 1"), k_eval("1 0 1")); + assert_eq!(k_eval("0N<1 0n 1"), k_eval("1 0 1")); + assert_eq!(k_eval("0N<0N"), k_eval("0")); + + println!("test_comparisons() dicts"); + assert_eq!(k_eval("1<`a`b!(1;2)"), k_eval("`a`b!(0;1)")); + assert_eq!(k_eval("1<`a`b!(1;2 3 4)"), k_eval("`a`b!(0;1 1 1)")); + assert_eq!(k_eval("(`a`b!(1;2))<2"), k_eval("`a`b!(1;0)")); + assert_eq!(k_eval("(`a`b!(1;2 3 4))<3"), k_eval("`a`b!(1;1 0 0)")); + assert_eq!(k_eval("(`a`b!(1;1))<(`a`b`c!(1;2;3 4 5))"), k_eval("`a`b`c!(0;1;1 1 1)")); + + assert_eq!(k_eval("(`a`b!(1;1))<(`a`b`c!(1;2;0N))"), k_eval("`a`b`c!(0;1;0)")); + assert_eq!(k_eval("(`a`b!(1;1))<(`a`b`c!(1;2;3))"), k_eval("`a`b`c!(0;1;1)")); + + println!("test_comparisons() tables"); + assert_eq!(k_eval("1<+`a`b!(1 1 1;2 3 4)"), k_eval("+`a`b!(0 0 0;1 1 1)")); + assert_eq!(k_eval("(+`a`b!(1 1 1;2 3 4))<3"), k_eval("+`a`b!(1 1 1;1 0 0)")); + + + println!("test_comparisons() numbers"); + assert_eq!(k_eval("1>2"), k_eval("0")); + assert_eq!(k_eval("1>1 2 3"), k_eval("0 0 0")); + assert_eq!(k_eval("1.0>2"), k_eval("0")); + + assert_eq!(k_eval("0N>1"), k_eval("0")); + assert_eq!(k_eval("0n>1"), k_eval("0")); + assert_eq!(k_eval("0n 0n>1"), k_eval("0 0")); + // 0N>0n is an annoyance to implement because of promote_nouns(). + // I'm going to leave it as 0=0N>0n for rok for convenience and assume it was just a quirk of the implementation for ngn-k. + // assert_eq!(k_eval("0N>0N"), k_eval("0")); + // assert_eq!(k_eval("0N>0n"), k_eval("1")); + // assert_eq!(k_eval("0n>0N"), k_eval("0")); + // assert_eq!(k_eval("0n<0N"), k_eval("1")); + assert_eq!(k_eval("0N>0N"), k_eval("0")); + assert_eq!(k_eval("0N>0n"), k_eval("0")); + assert_eq!(k_eval("0n>0N"), k_eval("0")); + assert_eq!(k_eval("0n<0N"), k_eval("0")); + assert_eq!(k_eval("0N>1 1 1"), k_eval("0 0 0")); + assert_eq!(k_eval("0N>1 0N 1"), k_eval("0 0 0")); + assert_eq!(k_eval("0N>1 0n 1"), k_eval("0 0 0")); + + println!("test_comparisons() dicts"); + assert_eq!(k_eval("1>`a`b!(1;2)"), k_eval("`a`b!(0;1)")); + assert_eq!(k_eval("1>`a`b!(1;2 3 4)"), k_eval("`a`b!(0;1 1 1)")); + assert_eq!(k_eval("(`a`b!(1;2))>2"), k_eval("`a`b!(1;0)")); + assert_eq!(k_eval("(`a`b!(1;2 3 4))>3"), k_eval("`a`b!(1;1 0 0)")); + assert_eq!(k_eval("(`a`b!(1;1))>(`a`b`c!(1;2;3 4 5))"), k_eval("`a`b`c!(0;1;1 1 1)")); + + assert_eq!(k_eval("(`a`b!(1;1))>(`a`b`c!(1;2;0N))"), k_eval("`a`b`c!(0;1;0)")); + assert_eq!(k_eval("(`a`b!(1;1))>(`a`b`c!(1;2;3))"), k_eval("`a`b`c!(0;1;1)")); + + println!("test_comparisons() tables"); + assert_eq!(k_eval("1>+`a`b!(1 1 1;2 3 4)"), k_eval("+`a`b!(0 0 0;1 1 1)")); + assert_eq!(k_eval("(+`a`b!(1 1 1;2 3 4))>3"), k_eval("+`a`b!(1 1 1;1 0 0)")); + +}