diff --git a/vlib/math/stats/stats.v b/vlib/math/stats/stats.v index ed071d6bacb640..6462a743ced615 100644 --- a/vlib/math/stats/stats.v +++ b/vlib/math/stats/stats.v @@ -434,7 +434,7 @@ pub fn covariance_mean[T](data1 []T, data2 []T, mean1 T, mean2 T) T { for i in 0 .. n { delta1 := data1[i] - mean1 delta2 := data2[i] - mean2 - covariance += T((delta1 * delta2 - covariance) / (i + T(1))) + covariance += T((delta1 * delta2 - covariance) / (T(i) + T(1))) } return covariance } @@ -459,8 +459,11 @@ pub fn lag1_autocorrelation_mean[T](data []T, mean T) T { for i := 1; i < data.len; i++ { delta0 := data[i - 1] - mean delta1 := data[i] - mean - q += T((delta0 * delta1 - q) / (i + T(1))) - v += T((delta1 * delta1 - v) / (T(i) + T(1))) + d01 := delta0 * delta1 + d11 := delta1 * delta1 + ti1 := T(i) + T(1) + q += T((d01 - q) / ti1) + v += T((d11 - v) / ti1) } return T(q / v) } @@ -486,7 +489,9 @@ pub fn kurtosis_mean_stddev[T](data []T, mean T, sd T) T { */ for i, v in data { x := (v - mean) / sd - avg += T((x * x * x * x - avg) / (i + T(1))) + x4 := x * x * x * x + ti1 := (T(i) + T(1)) + avg += T((x4 - avg) / ti1) } return avg - T(3) } @@ -511,7 +516,8 @@ pub fn skew_mean_stddev[T](data []T, mean T, sd T) T { */ for i, v in data { x := (v - mean) / sd - skew += T((x * x * x - skew) / (i + T(1))) + x3 := x * x * x + skew += T((x3 - skew) / (T(i) + T(1))) } return skew } diff --git a/vlib/math/stats/stats_test.v b/vlib/math/stats/stats_test.v index d77c773d0b106c..72944e6064dcc6 100644 --- a/vlib/math/stats/stats_test.v +++ b/vlib/math/stats/stats_test.v @@ -400,15 +400,15 @@ fn test_covariance() { mut data0 := [10.0, 4.45, 5.9, 2.7] mut data1 := [5.0, 14.45, -15.9, 22.7] mut o := stats.covariance(data0, data1) - assert math.alike(o, -17.37078207731247) + assert math.alike(o, -17.37078125) data0 = [-3.0, 67.31, 4.4, 1.89] data1 = [5.0, 77.31, 44.4, 11.89] o = stats.covariance(data0, data1) - assert math.alike(o, 740.0695419311523) + assert math.alike(o, 740.06955) data0 = [12.0, 7.88, 76.122, 54.83] data1 = [2.0, 5.88, 7.122, 5.83] o = stats.covariance(data0, data1) - assert math.alike(o, 36.65028190612793) + assert math.alike(o, 36.650282000000004) // test for int, i64, f32 array data0_int := [1, 2, 3, 1] @@ -428,13 +428,15 @@ fn test_covariance() { fn test_lag1_autocorrelation() { mut data := [10.0, 4.45, 5.9, 2.7] mut o := stats.lag1_autocorrelation(data) - assert math.alike(o, -0.554228566606572) + mut e := 0.0 + assert math.alike(o, -0.5542285481446095) data = [-3.0, 67.31, 4.4, 1.89] o = stats.lag1_autocorrelation(data) - assert math.alike(o, -0.5102510823460722) + assert math.alike(o, -0.5102510654033415) data = [12.0, 7.88, 76.122, 54.83] o = stats.lag1_autocorrelation(data) - assert math.alike(o, 0.10484451825170164) + e = 0.10484450460892072 + assert math.alike(o, e), diff(o, e) // test for int, i64, f32 array assert stats.lag1_autocorrelation[int]([1, 2, 3, 1]) == 0 @@ -443,40 +445,51 @@ fn test_lag1_autocorrelation() { assert math.alike(o, 0.1975308507680893) } +fn diff(actual f64, expected f64) string { + return '\n actual:${actual:40.35f}\nexpected:${expected:40.35f}\n diff:${actual - expected:40.35f}' +} + fn test_kurtosis() { mut data := [10.0, 4.45, 5.9, 2.7] mut o := stats.kurtosis(data) - assert math.alike(o, -1.0443214689384779) + mut e := -1.0443212849233845 + assert math.close(o, e), diff(o, e) data = [-3.0, 67.31, 4.4, 1.89] o = stats.kurtosis(data) - assert math.alike(o, -0.688495594786176) + e = -0.6884953374814851 + assert math.close(o, e), diff(o, e) data = [12.0, 7.88, 76.122, 54.83] o = stats.kurtosis(data) - assert math.alike(o, -1.7323772574195067) + assert math.alike(o, -1.7323772836921467) // test for int, i64, f32 array assert stats.kurtosis[int]([1, 2, 3, 1]) == 1 assert stats.kurtosis[i64]([i64(1), 2, 3, 1]) == 1 o = stats.kurtosis[f32]([f32(1.0), 3, 5, 7, 3]) - assert math.alike(o, -1.0443782806396484) + e = -1.044378399848938 + assert math.alike(o, e), diff(o, e) } fn test_skew() { mut data := [10.0, 4.45, 5.9, 2.7] mut o := stats.skew(data) - assert math.alike(o, 0.5754020379048158) + mut e := 0.5754021106320453 + assert math.veryclose(o, e), diff(o, e) data = [-3.0, 67.31, 4.4, 1.89] o = stats.skew(data) - assert math.alike(o, 1.1248732608899568) + e = 1.1248733711136492 + assert math.veryclose(o, e), diff(o, e) data = [12.0, 7.88, 76.122, 54.83] o = stats.skew(data) - assert math.alike(o, 0.19007917421924964) + e = 0.19007911706827735 + assert math.alike(o, e), diff(o, e) // test for int, i64, f32 array assert stats.skew[int]([1, 2, 3, 1]) == 2 assert stats.skew[i64]([i64(1), 2, 3, 1]) == 2 o = stats.skew[f32]([f32(1.0), 3, 5, 7, 3]) - assert math.alike(o, 0.2715454697608948) + e = 0.27154541015625 + assert math.alike(o, e), diff(o, e) } fn test_quantile() { @@ -484,13 +497,13 @@ fn test_quantile() { mut data := [2.7, 4.45, 5.9, 10.0] mut o := stats.quantile(data, 0.1)! - assert math.alike(o, 3.225000020861626) + assert math.alike(o, 3.225) data = [-3.0, 1.89, 4.4, 67.31] o = stats.quantile(data, 0.2)! - assert math.alike(o, -0.06599988341331486) + assert math.alike(o, -0.06599999999999961) data = [7.88, 12.0, 54.83, 76.122] o = stats.quantile(data, 0.3)! - assert math.alike(o, 11.587999901771546) + assert math.alike(o, 11.588) stats.quantile(data, -0.3) or { assert err.msg() == 'index out of range' } diff --git a/vlib/strconv/format.v b/vlib/strconv/format.v index 368cae42245647..9009da8aacbf64 100644 --- a/vlib/strconv/format.v +++ b/vlib/strconv/format.v @@ -43,6 +43,22 @@ const dec_round = [ 0.000000000000000005, 0.0000000000000000005, 0.00000000000000000005, + 0.000000000000000000005, + 0.0000000000000000000005, + 0.00000000000000000000005, + 0.000000000000000000000005, + 0.0000000000000000000000005, + 0.00000000000000000000000005, + 0.000000000000000000000000005, + 0.0000000000000000000000000005, + 0.00000000000000000000000000005, + 0.000000000000000000000000000005, + 0.0000000000000000000000000000005, + 0.00000000000000000000000000000005, + 0.000000000000000000000000000000005, + 0.0000000000000000000000000000000005, + 0.00000000000000000000000000000000005, + 0.000000000000000000000000000000000005, ]! // Single format functions diff --git a/vlib/strconv/format_mem.c.v b/vlib/strconv/format_mem.c.v index 1b5dca4200aa1f..e079c609a02769 100644 --- a/vlib/strconv/format_mem.c.v +++ b/vlib/strconv/format_mem.c.v @@ -209,7 +209,7 @@ pub fn f64_to_str_lnd1(f f64, dec_digit int) string { // allocate exp+32 chars for the return string // mut res := []u8{len:exp+32,init:`0`} - mut res := []u8{len: exp + 32, init: 0} + mut res := []u8{len: exp + 40, init: 0} mut r_i := 0 // result string buffer index // println("s:${sgn} b:${b[0]} es:${exp_sgn} exp:${exp}") diff --git a/vlib/v/checker/assign.v b/vlib/v/checker/assign.v index 2f897013ab8030..bdb270be7c0951 100644 --- a/vlib/v/checker/assign.v +++ b/vlib/v/checker/assign.v @@ -23,6 +23,12 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { node.left_types = [] mut right_len := node.right.len mut right_first_type := ast.void_type + old_recheck := c.inside_recheck + // check if we are rechecking an already checked expression on generic rechecking + c.inside_recheck = old_recheck || node.right_types.len > 0 + defer { + c.inside_recheck = old_recheck + } for i, mut right in node.right { if right in [ast.CallExpr, ast.IfExpr, ast.LockExpr, ast.MatchExpr, ast.DumpExpr, ast.SelectorExpr, ast.ParExpr, ast.ComptimeCall] { @@ -37,7 +43,9 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { } right_type_sym := c.table.sym(right_type) // fixed array returns an struct, but when assigning it must be the array type - right_type = c.cast_fixed_array_ret(right_type, right_type_sym) + if right_type_sym.info is ast.ArrayFixed { + right_type = c.cast_fixed_array_ret(right_type, right_type_sym) + } if i == 0 { right_first_type = right_type node.right_types = [ @@ -62,11 +70,23 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { } } } - } - if mut right is ast.InfixExpr { + } else if mut right is ast.InfixExpr { if right.op == .arrow { c.error('cannot use `<-` on the right-hand side of an assignment, as it does not return any values', right.pos) + } else if c.inside_recheck { + mut right_type := c.expr(mut right) + right_type_sym := c.table.sym(right_type) + // fixed array returns an struct, but when assigning it must be the array type + if right_type_sym.info is ast.ArrayFixed { + right_type = c.cast_fixed_array_ret(right_type, right_type_sym) + } + if i == 0 { + right_first_type = right_type + node.right_types = [ + c.check_expr_option_or_result_call(right, right_first_type), + ] + } } } if mut right is ast.Ident { @@ -182,16 +202,13 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { node.right_types << c.check_expr_option_or_result_call(node.right[i], right_type) } - } else { + } else if c.inside_recheck { // on generic recheck phase it might be needed to resolve the rhs again if i < node.right.len && c.comptime.has_comptime_expr(node.right[i]) { mut expr := mut node.right[i] - old_inside_recheck := c.inside_recheck - c.inside_recheck = true right_type := c.expr(mut expr) node.right_types[i] = c.check_expr_option_or_result_call(node.right[i], right_type) - c.inside_recheck = old_inside_recheck } } mut right := if i < node.right.len { node.right[i] } else { node.right[0] } @@ -393,6 +410,17 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { left.obj.ct_type_var = .field_var left.obj.typ = c.comptime.comptime_for_field_type } + } else if mut right is ast.InfixExpr { + right_ct_var := c.comptime.get_ct_type_var(right.left) + if right_ct_var != .no_comptime { + left.obj.ct_type_var = right_ct_var + } + } else if mut right is ast.IndexExpr + && c.comptime.is_comptime(right) { + right_ct_var := c.comptime.get_ct_type_var(right.left) + if right_ct_var != .no_comptime { + left.obj.ct_type_var = right_ct_var + } } else if mut right is ast.Ident && right.obj is ast.Var && right.or_expr.kind == .absent { right_obj_var := right.obj as ast.Var diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index a62d8b80f4b0bf..609dadb5e71e49 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -371,6 +371,19 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { left.obj.typ = var_type g.assign_ct_type = var_type } + } else if val is ast.InfixExpr && val.op in [.plus, .minus, .mul, .div, .mod] + && g.comptime.is_comptime(val.left) { + ctyp := g.unwrap_generic(g.type_resolver.get_type(val.left)) + if ctyp != ast.void_type { + ct_type_var := g.comptime.get_ct_type_var(val.left) + if ct_type_var in [.key_var, .value_var] { + g.type_resolver.update_ct_type(left.name, g.unwrap_generic(ctyp)) + } + var_type = ctyp + val_type = var_type + left.obj.typ = var_type + g.assign_ct_type = var_type + } } is_auto_heap = left.obj.is_auto_heap } diff --git a/vlib/v/gen/c/infix.v b/vlib/v/gen/c/infix.v index bb446e4ddea887..ad68ab8db72351 100644 --- a/vlib/v/gen/c/infix.v +++ b/vlib/v/gen/c/infix.v @@ -1188,8 +1188,10 @@ fn (mut g Gen) gen_plain_infix_expr(node ast.InfixExpr) { && node.op in [.plus, .minus, .mul, .div, .mod] && !(g.pref.translated || g.file.is_translated) if needs_cast { - typ_str := if g.comptime.is_comptime(node.left) { + typ_str := if !node.left.is_literal() && g.comptime.is_comptime(node.left) { g.styp(g.type_resolver.get_type_or_default(node.left, node.promoted_type)) + } else if !node.right.is_literal() && g.comptime.is_comptime(node.right) { + g.styp(g.type_resolver.get_type_or_default(node.right, node.promoted_type)) } else { g.styp(node.promoted_type) } diff --git a/vlib/v/tests/comptime/comptime_infix_assign_test.v b/vlib/v/tests/comptime/comptime_infix_assign_test.v new file mode 100644 index 00000000000000..eb8c08df435d96 --- /dev/null +++ b/vlib/v/tests/comptime/comptime_infix_assign_test.v @@ -0,0 +1,62 @@ +enum Flag { + usa_old_glory + all_other_bad_excuses_for_a_flag +} + +struct Test { + is_foo bool + name [5]u8 +} + +fn enc[T](item T) string { + $if T is $int { + len := match typeof(item).name { + 'i8', 'u8' { u8(2) } + 'i16', 'u16' { 4 } + 'int', 'u32', 'i32' { 8 } + 'i64', 'u64' { 16 } + else { return '' } + } + return u64_to_hex(item, len) + } $else $if T is $array { + mut hex := '' + for val in item { + hex += enc(val) + } + return hex + } $else $if T is $struct { + mut hex := '' + $for field in T.fields { + hex += enc(item.$(field.name)) + '_' + } + return hex + } $else { + if typeof(item).name == 'bool' { + return enc(int(item)) + } + $if debug { + println('cannot encode ${T}(s)') + } + return '' + } +} + +@[direct_array_access; inline] +fn u64_to_hex(nn u64, len u8) string { + mut n := nn + mut buf := [17]u8{} + buf[len] = 0 + mut i := 0 + for i = len - 1; i >= 0; i-- { + d := u8(n & 0xF) + buf[i] = if d < 10 { d + `0` } else { d + 87 } + n = n >> 4 + } + return unsafe { tos(memdup(&buf[0], len + 1), len) } +} + +fn test_main() { + assert enc(Test{}) == '00000000_0000000000_' + assert enc(Test{ is_foo: true }) == '00000001_0000000000_' + assert enc(Test{ name: [u8(1), 2, 3, 4, 5]! }) == '00000000_0102030405_' +} diff --git a/vlib/v/type_resolver/comptime_resolver.v b/vlib/v/type_resolver/comptime_resolver.v index 46c7cd91d51868..c7cf6b0236df60 100644 --- a/vlib/v/type_resolver/comptime_resolver.v +++ b/vlib/v/type_resolver/comptime_resolver.v @@ -50,7 +50,11 @@ pub fn (t &ResolverInfo) is_comptime(node ast.Expr) bool { return node.expr is ast.Ident && node.expr.ct_expr } ast.InfixExpr { - return t.is_comptime(node.left) || t.is_comptime(node.right) + if node.op in [.plus, .minus, .mul, .div, .mod] { + t.is_comptime(node.left) || t.is_comptime(node.right) + } else { + false + } } ast.ParExpr { return t.is_comptime(node.expr) @@ -76,6 +80,10 @@ pub fn (t &ResolverInfo) get_ct_type_var(node ast.Expr) ast.ComptimeVarKind { } } else if node is ast.IndexExpr { return t.get_ct_type_var(node.left) + } else if node is ast.InfixExpr { + return t.get_ct_type_var(node.left) + } else if node is ast.ParExpr { + return t.get_ct_type_var(node.expr) } return .no_comptime } diff --git a/vlib/v/type_resolver/generic_resolver.v b/vlib/v/type_resolver/generic_resolver.v index dbbce60de0a3c0..d4cd21dbf28dfc 100644 --- a/vlib/v/type_resolver/generic_resolver.v +++ b/vlib/v/type_resolver/generic_resolver.v @@ -303,7 +303,7 @@ pub fn (mut t TypeResolver) resolve_args(cur_fn &ast.FnDecl, func &ast.Fn, mut n comptime_args[k] = m.return_type } } - } else if mut call_arg.expr is ast.CastExpr { + } else if mut call_arg.expr is ast.CastExpr && call_arg.expr.typ.has_flag(.generic) { cparam_type_sym := t.table.sym(t.resolver.unwrap_generic(call_arg.expr.typ)) param_typ_sym := t.table.sym(param_typ) if param_typ_sym.kind == .map && cparam_type_sym.info is ast.Map { diff --git a/vlib/v/type_resolver/type_resolver.v b/vlib/v/type_resolver/type_resolver.v index fc0225faa0f937..0bdeb8b48d4bbf 100644 --- a/vlib/v/type_resolver/type_resolver.v +++ b/vlib/v/type_resolver/type_resolver.v @@ -115,9 +115,25 @@ pub fn (mut t TypeResolver) get_type_or_default(node ast.Expr, default_typ ast.T return t.get_type_or_default(node.expr, default_typ) } ast.InfixExpr { - if node.op in [.plus, .minus, .mul, .div, .mod] { + if !node.left.is_literal() && node.op in [.plus, .minus, .mul, .div, .mod] { return t.get_type_or_default(node.left, default_typ) } + if !node.right.is_literal() && node.op in [.plus, .minus, .mul, .div, .mod] { + return t.get_type_or_default(node.right, default_typ) + } + } + ast.IndexExpr { + if node.left is ast.Ident && node.left.ct_expr { + ctyp := t.get_type(node) + if ctyp != ast.void_type { + return ctyp + } + } + } + ast.CastExpr { + if node.typ.has_flag(.generic) { + return t.resolver.unwrap_generic(node.typ) + } } else { return default_typ @@ -184,6 +200,16 @@ pub fn (mut t TypeResolver) get_type(node ast.Expr) ast.Type { nltype := t.get_type(node.left) nltype_unwrapped := t.resolver.unwrap_generic(nltype) return t.table.value_type(nltype_unwrapped) + } else if node is ast.ParExpr && t.info.is_comptime(node.expr) { + return t.get_type(node.expr) + } else if node is ast.InfixExpr { + if !node.left.is_literal() && t.info.is_comptime(node.left) { + return t.get_type(node.left) + } else if !node.right.is_literal() && t.info.is_comptime(node.right) { + return t.get_type(node.right) + } + } else if node is ast.CastExpr && node.typ.has_flag(.generic) { + return t.resolver.unwrap_generic(node.typ) } return ast.void_type }