Merge branch 'trunk' of github.com:rtfeldman/roc into crates-folder

This commit is contained in:
Anton-4 2022-07-02 10:44:25 +02:00
commit b1b9a8dc0a
No known key found for this signature in database
GPG key ID: C954D6E0F9C0ABFD
29 changed files with 358 additions and 156 deletions

View file

@ -803,6 +803,12 @@ pub fn listSingle(alignment: u32, element: Opaque, element_width: usize) callcon
return output;
}
pub fn listWithCapacity(capacity: usize, alignment: u32, element_width: usize) callconv(.C) RocList {
var output = RocList.allocate(alignment, capacity, element_width);
output.length = 0;
return output;
}
pub fn listAppend(list: RocList, alignment: u32, element: Opaque, element_width: usize, update_mode: UpdateMode) callconv(.C) RocList {
const old_length = list.len();
var output: RocList = undefined;

View file

@ -52,6 +52,7 @@ comptime {
exportListFn(list.listAppend, "append");
exportListFn(list.listPrepend, "prepend");
exportListFn(list.listSingle, "single");
exportListFn(list.listWithCapacity, "with_capacity");
exportListFn(list.listJoin, "join");
exportListFn(list.listRange, "range");
exportListFn(list.listReverse, "reverse");

View file

@ -8,6 +8,7 @@ interface List
append,
map,
len,
withCapacity,
walkBackwards,
concat,
first,
@ -259,6 +260,9 @@ prepend : List a, a -> List a
## returns can always be safely converted to an #I32 without losing any data.
len : List a -> Nat
## Create a list with space for at least capacity elements
withCapacity : Nat -> List a
## Put two lists together.
##
## >>> List.concat [1, 2, 3] [4, 5]

View file

@ -367,6 +367,7 @@ pub const LIST_SUBLIST: &str = "roc_builtins.list.sublist";
pub const LIST_DROP_AT: &str = "roc_builtins.list.drop_at";
pub const LIST_SWAP: &str = "roc_builtins.list.swap";
pub const LIST_SINGLE: &str = "roc_builtins.list.single";
pub const LIST_WITH_CAPACITY: &str = "roc_builtins.list.with_capacity";
pub const LIST_JOIN: &str = "roc_builtins.list.join";
pub const LIST_RANGE: &str = "roc_builtins.list.range";
pub const LIST_REVERSE: &str = "roc_builtins.list.reverse";

View file

@ -102,6 +102,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
STR_TO_U8 => str_to_num,
STR_TO_I8 => str_to_num,
LIST_LEN => list_len,
LIST_WITH_CAPACITY => list_with_capacity,
LIST_GET_UNSAFE => list_get_unsafe,
LIST_REPLACE_UNSAFE => list_replace_unsafe,
LIST_SET => list_set,
@ -2150,24 +2151,14 @@ fn list_single(symbol: Symbol, var_store: &mut VarStore) -> Def {
)
}
/// List.len : List * -> Int
/// List.len : List a -> Nat
fn list_len(symbol: Symbol, var_store: &mut VarStore) -> Def {
let len_var = var_store.fresh();
let list_var = var_store.fresh();
lowlevel_1(symbol, LowLevel::ListLen, var_store)
}
let body = RunLowLevel {
op: LowLevel::ListLen,
args: vec![(list_var, Var(Symbol::ARG_1))],
ret_var: len_var,
};
defn(
symbol,
vec![(list_var, Symbol::ARG_1)],
var_store,
body,
len_var,
)
/// List.withCapacity : Nat -> List a
fn list_with_capacity(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_1(symbol, LowLevel::ListWithCapacity, var_store)
}
/// List.getUnsafe : List elem, Int -> elem

View file

@ -13,6 +13,7 @@ use crate::llvm::build_list::{
list_keep_if, list_keep_oks, list_len, list_map, list_map2, list_map3, list_map4,
list_map_with_index, list_prepend, list_range, list_repeat, list_replace_unsafe, list_reverse,
list_single, list_sort_with, list_sublist, list_swap, list_symbol_to_c_abi, list_to_c_abi,
list_with_capacity,
};
use crate::llvm::build_str::{
str_from_float, str_from_int, str_from_utf8, str_from_utf8_range, str_split,
@ -5585,6 +5586,15 @@ fn run_low_level<'a, 'ctx, 'env>(
list_len(env.builder, arg.into_struct_value()).into()
}
ListWithCapacity => {
// List.withCapacity : Nat -> List a
debug_assert_eq!(args.len(), 1);
let list_len = load_symbol(scope, &args[0]).into_int_value();
let result_layout = *layout;
list_with_capacity(env, list_len, &list_element_layout!(result_layout))
}
ListSingle => {
// List.single : a -> List a
debug_assert_eq!(args.len(), 1);

View file

@ -123,6 +123,22 @@ pub fn list_single<'a, 'ctx, 'env>(
)
}
pub fn list_with_capacity<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
capacity: IntValue<'ctx>,
element_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
call_list_bitcode_fn(
env,
&[
capacity.into(),
env.alignment_intvalue(element_layout),
layout_width(env, element_layout),
],
bitcode::LIST_WITH_CAPACITY,
)
}
/// List.repeat : elem, Nat -> List elem
pub fn list_repeat<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,

View file

@ -425,6 +425,27 @@ impl<'a> LowLevelCall<'a> {
let elems = backend.env.arena.alloc([ListLiteralElement::Symbol(elem)]);
backend.expr_array(self.ret_symbol, &self.ret_storage, elem_layout, elems)
}
ListWithCapacity => {
// List.withCapacity : Nat -> List elem
let capacity: Symbol = self.arguments[0];
let elem_layout = unwrap_list_elem_layout(self.ret_layout);
let (elem_width, elem_align) = elem_layout.stack_size_and_alignment(TARGET_INFO);
// Zig arguments Wasm types
// (return pointer) i32
// capacity: usize i32
// alignment: u32 i32
// element_width: usize i32
backend
.storage
.load_symbols(&mut backend.code_builder, &[self.ret_symbol, capacity]);
backend.code_builder.i32_const(elem_align as i32);
backend.code_builder.i32_const(elem_width as i32);
backend.call_host_fn_after_loading_args(bitcode::LIST_WITH_CAPACITY, 4, false);
}
ListRepeat => {
// List.repeat : elem, Nat -> List elem

View file

@ -24,6 +24,7 @@ pub enum LowLevel {
StrTrimRight,
StrToNum,
ListLen,
ListWithCapacity,
ListGetUnsafe,
ListSingle,
ListRepeat,

View file

@ -1253,6 +1253,7 @@ define_builtins! {
59 LIST_FIND_INDEX: "findIndex"
60 LIST_GET_UNSAFE: "getUnsafe"
61 LIST_REPLACE_UNSAFE: "replaceUnsafe"
62 LIST_WITH_CAPACITY: "withCapacity"
}
6 RESULT: "Result" => {
0 RESULT_RESULT: "Result" // the Result.Result type alias

View file

@ -897,6 +897,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
// - other refcounted arguments are Borrowed
match op {
ListLen | StrIsEmpty | StrCountGraphemes => arena.alloc_slice_copy(&[borrowed]),
ListWithCapacity => arena.alloc_slice_copy(&[irrelevant]),
ListReplaceUnsafe => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]),
ListConcat => arena.alloc_slice_copy(&[owned, owned]),

View file

@ -2939,3 +2939,22 @@ fn monomorphized_lists() {
u64
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn with_capacity() {
// see https://github.com/rtfeldman/roc/issues/1732
assert_evals_to!(
indoc!(
r#"
List.withCapacity 10
|> List.append 0u64
|> List.append 1u64
|> List.append 2u64
|> List.append 3u64
"#
),
RocList::from_slice(&[0, 1, 2, 3]),
RocList<u64>
);
}

View file

@ -1,6 +1,6 @@
procedure List.6 (#Attr.2):
let List.162 : U64 = lowlevel ListLen #Attr.2;
ret List.162;
let List.164 : U64 = lowlevel ListLen #Attr.2;
ret List.164;
procedure Test.1 (Test.5):
let Test.2 : I64 = 41i64;

View file

@ -1,22 +1,22 @@
procedure List.2 (List.66, List.67):
let List.168 : U64 = CallByName List.6 List.66;
let List.164 : Int1 = CallByName Num.22 List.67 List.168;
if List.164 then
let List.166 : {} = CallByName List.60 List.66 List.67;
let List.165 : [C {}, C {}] = Ok List.166;
ret List.165;
procedure List.2 (List.67, List.68):
let List.170 : U64 = CallByName List.6 List.67;
let List.166 : Int1 = CallByName Num.22 List.68 List.170;
if List.166 then
let List.168 : {} = CallByName List.60 List.67 List.68;
let List.167 : [C {}, C {}] = Ok List.168;
ret List.167;
else
let List.163 : {} = Struct {};
let List.162 : [C {}, C {}] = Err List.163;
ret List.162;
let List.165 : {} = Struct {};
let List.164 : [C {}, C {}] = Err List.165;
ret List.164;
procedure List.6 (#Attr.2):
let List.171 : U64 = lowlevel ListLen #Attr.2;
ret List.171;
let List.173 : U64 = lowlevel ListLen #Attr.2;
ret List.173;
procedure List.60 (#Attr.2, #Attr.3):
let List.170 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.170;
let List.172 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.172;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.273 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;

View file

@ -1,6 +1,6 @@
procedure List.4 (#Attr.2, #Attr.3):
let List.162 : List U8 = lowlevel ListAppend #Attr.2 #Attr.3;
ret List.162;
let List.164 : List U8 = lowlevel ListAppend #Attr.2 #Attr.3;
ret List.164;
procedure Test.20 (Test.22):
let Test.34 : {U8} = Struct {Test.22};

View file

@ -1,6 +1,6 @@
procedure List.6 (#Attr.2):
let List.162 : U64 = lowlevel ListLen #Attr.2;
ret List.162;
let List.164 : U64 = lowlevel ListLen #Attr.2;
ret List.164;
procedure Num.19 (#Attr.2, #Attr.3):
let Num.275 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;

View file

@ -1,16 +1,16 @@
procedure List.9 (#Attr.2):
let List.168 : U64 = 0i64;
let List.169 : U64 = lowlevel ListLen #Attr.2;
let List.164 : Int1 = lowlevel NotEq List.168 List.169;
if List.164 then
let List.167 : U64 = 0i64;
let List.166 : I64 = lowlevel ListGetUnsafe #Attr.2 List.167;
let List.165 : [C Int1, C I64] = Ok List.166;
ret List.165;
let List.170 : U64 = 0i64;
let List.171 : U64 = lowlevel ListLen #Attr.2;
let List.166 : Int1 = lowlevel NotEq List.170 List.171;
if List.166 then
let List.169 : U64 = 0i64;
let List.168 : I64 = lowlevel ListGetUnsafe #Attr.2 List.169;
let List.167 : [C Int1, C I64] = Ok List.168;
ret List.167;
else
let List.163 : Int1 = true;
let List.162 : [C Int1, C I64] = Err List.163;
ret List.162;
let List.165 : Int1 = true;
let List.164 : [C Int1, C I64] = Err List.165;
ret List.164;
procedure Str.27 (#Attr.2):
let #Attr.3 : {I64, U8} = lowlevel StrToNum #Attr.2;

View file

@ -1,6 +1,6 @@
procedure List.4 (#Attr.2, #Attr.3):
let List.162 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3;
ret List.162;
let List.164 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3;
ret List.164;
procedure Test.0 ():
let Test.2 : List I64 = Array [1i64];

View file

@ -1,6 +1,6 @@
procedure List.4 (#Attr.2, #Attr.3):
let List.162 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3;
ret List.162;
let List.164 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3;
ret List.164;
procedure Test.1 (Test.2):
let Test.6 : I64 = 42i64;

View file

@ -1,10 +1,10 @@
procedure List.6 (#Attr.2):
let List.162 : U64 = lowlevel ListLen #Attr.2;
ret List.162;
let List.164 : U64 = lowlevel ListLen #Attr.2;
ret List.164;
procedure List.6 (#Attr.2):
let List.163 : U64 = lowlevel ListLen #Attr.2;
ret List.163;
let List.165 : U64 = lowlevel ListLen #Attr.2;
ret List.165;
procedure Num.19 (#Attr.2, #Attr.3):
let Num.273 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;

View file

@ -1,26 +1,26 @@
procedure List.2 (List.66, List.67):
let List.168 : U64 = CallByName List.6 List.66;
let List.164 : Int1 = CallByName Num.22 List.67 List.168;
if List.164 then
let List.166 : Str = CallByName List.60 List.66 List.67;
let List.165 : [C {}, C Str] = Ok List.166;
ret List.165;
procedure List.2 (List.67, List.68):
let List.170 : U64 = CallByName List.6 List.67;
let List.166 : Int1 = CallByName Num.22 List.68 List.170;
if List.166 then
let List.168 : Str = CallByName List.60 List.67 List.68;
let List.167 : [C {}, C Str] = Ok List.168;
ret List.167;
else
let List.163 : {} = Struct {};
let List.162 : [C {}, C Str] = Err List.163;
ret List.162;
let List.165 : {} = Struct {};
let List.164 : [C {}, C Str] = Err List.165;
ret List.164;
procedure List.5 (#Attr.2, #Attr.3):
let List.170 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
ret List.170;
procedure List.6 (#Attr.2):
let List.172 : U64 = lowlevel ListLen #Attr.2;
let List.172 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
ret List.172;
procedure List.6 (#Attr.2):
let List.174 : U64 = lowlevel ListLen #Attr.2;
ret List.174;
procedure List.60 (#Attr.2, #Attr.3):
let List.171 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.171;
let List.173 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.173;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.273 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;

View file

@ -1,28 +1,28 @@
procedure List.2 (List.66, List.67):
let List.168 : U64 = CallByName List.6 List.66;
let List.164 : Int1 = CallByName Num.22 List.67 List.168;
if List.164 then
let List.166 : Str = CallByName List.60 List.66 List.67;
let List.165 : [C {}, C Str] = Ok List.166;
ret List.165;
procedure List.2 (List.67, List.68):
let List.170 : U64 = CallByName List.6 List.67;
let List.166 : Int1 = CallByName Num.22 List.68 List.170;
if List.166 then
let List.168 : Str = CallByName List.60 List.67 List.68;
let List.167 : [C {}, C Str] = Ok List.168;
ret List.167;
else
let List.163 : {} = Struct {};
let List.162 : [C {}, C Str] = Err List.163;
ret List.162;
let List.165 : {} = Struct {};
let List.164 : [C {}, C Str] = Err List.165;
ret List.164;
procedure List.5 (#Attr.2, #Attr.3):
inc #Attr.2;
let List.170 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
let List.172 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
decref #Attr.2;
ret List.170;
procedure List.6 (#Attr.2):
let List.172 : U64 = lowlevel ListLen #Attr.2;
ret List.172;
procedure List.6 (#Attr.2):
let List.174 : U64 = lowlevel ListLen #Attr.2;
ret List.174;
procedure List.60 (#Attr.2, #Attr.3):
let List.171 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.171;
let List.173 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.173;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.273 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;

View file

@ -1,16 +1,16 @@
procedure List.28 (#Attr.2, #Attr.3):
let List.165 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3;
let List.167 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3;
let Bool.14 : Int1 = lowlevel ListIsUnique #Attr.2;
if Bool.14 then
ret List.165;
ret List.167;
else
decref #Attr.2;
ret List.165;
ret List.167;
procedure List.54 (List.109):
let List.163 : {} = Struct {};
let List.162 : List I64 = CallByName List.28 List.109 List.163;
ret List.162;
procedure List.54 (List.111):
let List.165 : {} = Struct {};
let List.164 : List I64 = CallByName List.28 List.111 List.165;
ret List.164;
procedure Num.46 (#Attr.2, #Attr.3):
let Num.273 : U8 = lowlevel NumCompare #Attr.2 #Attr.3;

View file

@ -1,43 +1,43 @@
procedure List.2 (List.66, List.67):
let List.182 : U64 = CallByName List.6 List.66;
let List.178 : Int1 = CallByName Num.22 List.67 List.182;
if List.178 then
let List.180 : I64 = CallByName List.60 List.66 List.67;
let List.179 : [C {}, C I64] = Ok List.180;
ret List.179;
procedure List.2 (List.67, List.68):
let List.184 : U64 = CallByName List.6 List.67;
let List.180 : Int1 = CallByName Num.22 List.68 List.184;
if List.180 then
let List.182 : I64 = CallByName List.60 List.67 List.68;
let List.181 : [C {}, C I64] = Ok List.182;
ret List.181;
else
let List.177 : {} = Struct {};
let List.176 : [C {}, C I64] = Err List.177;
ret List.176;
let List.179 : {} = Struct {};
let List.178 : [C {}, C I64] = Err List.179;
ret List.178;
procedure List.3 (List.75, List.76, List.77):
let List.166 : {List I64, I64} = CallByName List.57 List.75 List.76 List.77;
let List.165 : List I64 = StructAtIndex 0 List.166;
inc List.165;
dec List.166;
ret List.165;
procedure List.3 (List.76, List.77, List.78):
let List.168 : {List I64, I64} = CallByName List.57 List.76 List.77 List.78;
let List.167 : List I64 = StructAtIndex 0 List.168;
inc List.167;
dec List.168;
ret List.167;
procedure List.57 (List.72, List.73, List.74):
let List.188 : U64 = CallByName List.6 List.72;
let List.185 : Int1 = CallByName Num.22 List.73 List.188;
if List.185 then
let List.186 : {List I64, I64} = CallByName List.61 List.72 List.73 List.74;
procedure List.57 (List.73, List.74, List.75):
let List.190 : U64 = CallByName List.6 List.73;
let List.187 : Int1 = CallByName Num.22 List.74 List.190;
if List.187 then
let List.188 : {List I64, I64} = CallByName List.61 List.73 List.74 List.75;
ret List.188;
else
let List.186 : {List I64, I64} = Struct {List.73, List.75};
ret List.186;
else
let List.184 : {List I64, I64} = Struct {List.72, List.74};
ret List.184;
procedure List.6 (#Attr.2):
let List.189 : U64 = lowlevel ListLen #Attr.2;
ret List.189;
let List.191 : U64 = lowlevel ListLen #Attr.2;
ret List.191;
procedure List.60 (#Attr.2, #Attr.3):
let List.190 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.190;
let List.192 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.192;
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
let List.187 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.187;
let List.189 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.189;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.275 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;

View file

@ -1,43 +1,43 @@
procedure List.2 (List.66, List.67):
let List.182 : U64 = CallByName List.6 List.66;
let List.178 : Int1 = CallByName Num.22 List.67 List.182;
if List.178 then
let List.180 : I64 = CallByName List.60 List.66 List.67;
let List.179 : [C {}, C I64] = Ok List.180;
ret List.179;
procedure List.2 (List.67, List.68):
let List.184 : U64 = CallByName List.6 List.67;
let List.180 : Int1 = CallByName Num.22 List.68 List.184;
if List.180 then
let List.182 : I64 = CallByName List.60 List.67 List.68;
let List.181 : [C {}, C I64] = Ok List.182;
ret List.181;
else
let List.177 : {} = Struct {};
let List.176 : [C {}, C I64] = Err List.177;
ret List.176;
let List.179 : {} = Struct {};
let List.178 : [C {}, C I64] = Err List.179;
ret List.178;
procedure List.3 (List.75, List.76, List.77):
let List.166 : {List I64, I64} = CallByName List.57 List.75 List.76 List.77;
let List.165 : List I64 = StructAtIndex 0 List.166;
inc List.165;
dec List.166;
ret List.165;
procedure List.3 (List.76, List.77, List.78):
let List.168 : {List I64, I64} = CallByName List.57 List.76 List.77 List.78;
let List.167 : List I64 = StructAtIndex 0 List.168;
inc List.167;
dec List.168;
ret List.167;
procedure List.57 (List.72, List.73, List.74):
let List.188 : U64 = CallByName List.6 List.72;
let List.185 : Int1 = CallByName Num.22 List.73 List.188;
if List.185 then
let List.186 : {List I64, I64} = CallByName List.61 List.72 List.73 List.74;
procedure List.57 (List.73, List.74, List.75):
let List.190 : U64 = CallByName List.6 List.73;
let List.187 : Int1 = CallByName Num.22 List.74 List.190;
if List.187 then
let List.188 : {List I64, I64} = CallByName List.61 List.73 List.74 List.75;
ret List.188;
else
let List.186 : {List I64, I64} = Struct {List.73, List.75};
ret List.186;
else
let List.184 : {List I64, I64} = Struct {List.72, List.74};
ret List.184;
procedure List.6 (#Attr.2):
let List.189 : U64 = lowlevel ListLen #Attr.2;
ret List.189;
let List.191 : U64 = lowlevel ListLen #Attr.2;
ret List.191;
procedure List.60 (#Attr.2, #Attr.3):
let List.190 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.190;
let List.192 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.192;
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
let List.187 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.187;
let List.189 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.189;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.275 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;