| author | |
| committer | |
| log | 06c4b35eb12a54a4e260a80ed8ed21eb5bfae09a |
| tree | 33d8366daa35741ce302df23e2be1e217aa20187 |
| parent | bc81ddfea67db0b3756027e98cc00bb8fa903a20 |
9 files changed, 200 insertions(+), 79 deletions(-)
example/guess_number/main.zig+4-6| ... | @@ -6,13 +6,11 @@ const os = std.os; | ... | @@ -6,13 +6,11 @@ const os = std.os; |
| 6 | pub fn main(args: [][]u8) -> %void { | 6 | pub fn main(args: [][]u8) -> %void { |
| 7 | %%io.stdout.printf("Welcome to the Guess Number Game in Zig.\n"); | 7 | %%io.stdout.printf("Welcome to the Guess Number Game in Zig.\n"); |
| 8 | 8 | ||
| 9 | var seed : u32 = undefined; | 9 | var seed: [@sizeof(usize)]u8 = undefined; |
| 10 | const seed_bytes = (&u8)(&seed)[0...4]; | 10 | %%os.get_random_bytes(seed); |
| 11 | %%os.get_random_bytes(seed_bytes); | 11 | var rand = Rand.init(([]usize)(seed)[0]); |
| 12 | 12 | ||
| 13 | var rand = Rand.init(seed); | 13 | const answer = rand.range_unsigned(u8, 0, 100) + 1; |
| 14 | |||
| 15 | const answer = rand.range_u64(0, 100) + 1; | ||
| 16 | 14 | ||
| 17 | while (true) { | 15 | while (true) { |
| 18 | %%io.stdout.printf("\nGuess a number between 1 and 100: "); | 16 | %%io.stdout.printf("\nGuess a number between 1 and 100: "); |
src/all_types.hpp+2| ... | @@ -387,6 +387,7 @@ enum CastOp { | ... | @@ -387,6 +387,7 @@ enum CastOp { |
| 387 | CastOpBoolToInt, | 387 | CastOpBoolToInt, |
| 388 | CastOpResizeSlice, | 388 | CastOpResizeSlice, |
| 389 | CastOpIntToEnum, | 389 | CastOpIntToEnum, |
| 390 | CastOpBytesToSlice, | ||
| 390 | }; | 391 | }; |
| 391 | 392 | ||
| 392 | struct AstNodeFnCallExpr { | 393 | struct AstNodeFnCallExpr { |
| ... | @@ -1311,6 +1312,7 @@ struct VariableTableEntry { | ... | @@ -1311,6 +1312,7 @@ struct VariableTableEntry { |
| 1311 | int gen_arg_index; | 1312 | int gen_arg_index; |
| 1312 | BlockContext *block_context; | 1313 | BlockContext *block_context; |
| 1313 | LLVMValueRef param_value_ref; | 1314 | LLVMValueRef param_value_ref; |
| 1315 | bool force_depends_on_compile_var; | ||
| 1314 | }; | 1316 | }; |
| 1315 | 1317 | ||
| 1316 | struct ErrorTableEntry { | 1318 | struct ErrorTableEntry { |
src/analyze.cpp+31-7| ... | @@ -3038,7 +3038,7 @@ static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, Variabl | ... | @@ -3038,7 +3038,7 @@ static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, Variabl |
| 3038 | ConstExprValue *other_const_val = &get_resolved_expr(var->val_node)->const_val; | 3038 | ConstExprValue *other_const_val = &get_resolved_expr(var->val_node)->const_val; |
| 3039 | if (other_const_val->ok) { | 3039 | if (other_const_val->ok) { |
| 3040 | return resolve_expr_const_val_as_other_expr(g, source_node, var->val_node, | 3040 | return resolve_expr_const_val_as_other_expr(g, source_node, var->val_node, |
| 3041 | depends_on_compile_var); | 3041 | depends_on_compile_var || var->force_depends_on_compile_var); |
| 3042 | } | 3042 | } |
| 3043 | } | 3043 | } |
| 3044 | return var->type; | 3044 | return var->type; |
| ... | @@ -3959,12 +3959,12 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, | ... | @@ -3959,12 +3959,12 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, |
| 3959 | { | 3959 | { |
| 3960 | assert(node->type == NodeTypeWhileExpr); | 3960 | assert(node->type == NodeTypeWhileExpr); |
| 3961 | 3961 | ||
| 3962 | AstNode *condition_node = node->data.while_expr.condition; | 3962 | AstNode **condition_node = &node->data.while_expr.condition; |
| 3963 | AstNode *while_body_node = node->data.while_expr.body; | 3963 | AstNode *while_body_node = node->data.while_expr.body; |
| 3964 | AstNode **continue_expr_node = &node->data.while_expr.continue_expr; | 3964 | AstNode **continue_expr_node = &node->data.while_expr.continue_expr; |
| 3965 | 3965 | ||
| 3966 | TypeTableEntry *condition_type = analyze_expression(g, import, context, | 3966 | TypeTableEntry *condition_type = analyze_expression(g, import, context, |
| 3967 | g->builtin_types.entry_bool, condition_node); | 3967 | g->builtin_types.entry_bool, *condition_node); |
| 3968 | 3968 | ||
| 3969 | if (*continue_expr_node) { | 3969 | if (*continue_expr_node) { |
| 3970 | analyze_expression(g, import, context, g->builtin_types.entry_void, *continue_expr_node); | 3970 | analyze_expression(g, import, context, g->builtin_types.entry_void, *continue_expr_node); |
| ... | @@ -3983,7 +3983,7 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, | ... | @@ -3983,7 +3983,7 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, |
| 3983 | } else { | 3983 | } else { |
| 3984 | // if the condition is a simple constant expression and there are no break statements | 3984 | // if the condition is a simple constant expression and there are no break statements |
| 3985 | // then the return type is unreachable | 3985 | // then the return type is unreachable |
| 3986 | ConstExprValue *const_val = &get_resolved_expr(condition_node)->const_val; | 3986 | ConstExprValue *const_val = &get_resolved_expr(*condition_node)->const_val; |
| 3987 | if (const_val->ok) { | 3987 | if (const_val->ok) { |
| 3988 | if (const_val->data.x_bool) { | 3988 | if (const_val->data.x_bool) { |
| 3989 | node->data.while_expr.condition_always_true = true; | 3989 | node->data.while_expr.condition_always_true = true; |
| ... | @@ -4392,6 +4392,24 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B | ... | @@ -4392,6 +4392,24 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B |
| 4392 | return resolve_cast(g, context, node, expr_node, wanted_type, CastOpResizeSlice, true); | 4392 | return resolve_cast(g, context, node, expr_node, wanted_type, CastOpResizeSlice, true); |
| 4393 | } | 4393 | } |
| 4394 | 4394 | ||
| 4395 | // explicit cast from [N]u8 to []T | ||
| 4396 | if (is_slice(wanted_type) && | ||
| 4397 | actual_type->id == TypeTableEntryIdArray && | ||
| 4398 | is_u8(actual_type->data.array.child_type)) | ||
| 4399 | { | ||
| 4400 | mark_impure_fn(context); | ||
| 4401 | uint64_t child_type_size = type_size(g, | ||
| 4402 | wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type); | ||
| 4403 | if (actual_type->data.array.len % child_type_size == 0) { | ||
| 4404 | return resolve_cast(g, context, node, expr_node, wanted_type, CastOpBytesToSlice, true); | ||
| 4405 | } else { | ||
| 4406 | add_node_error(g, node, | ||
| 4407 | buf_sprintf("unable to convert %s to %s: size mismatch", | ||
| 4408 | buf_ptr(&actual_type->name), buf_ptr(&wanted_type->name))); | ||
| 4409 | return g->builtin_types.entry_invalid; | ||
| 4410 | } | ||
| 4411 | } | ||
| 4412 | |||
| 4395 | // explicit cast from pointer to another pointer | 4413 | // explicit cast from pointer to another pointer |
| 4396 | if ((actual_type->id == TypeTableEntryIdPointer || actual_type->id == TypeTableEntryIdFn) && | 4414 | if ((actual_type->id == TypeTableEntryIdPointer || actual_type->id == TypeTableEntryIdFn) && |
| 4397 | (wanted_type->id == TypeTableEntryIdPointer || wanted_type->id == TypeTableEntryIdFn)) | 4415 | (wanted_type->id == TypeTableEntryIdPointer || wanted_type->id == TypeTableEntryIdFn)) |
| ... | @@ -5062,8 +5080,10 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry | ... | @@ -5062,8 +5080,10 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry |
| 5062 | return g->builtin_types.entry_invalid; | 5080 | return g->builtin_types.entry_invalid; |
| 5063 | } else { | 5081 | } else { |
| 5064 | uint64_t size_in_bytes = type_size(g, type_entry); | 5082 | uint64_t size_in_bytes = type_size(g, type_entry); |
| 5083 | bool depends_on_compile_var = (type_entry == g->builtin_types.entry_usize || | ||
| 5084 | type_entry == g->builtin_types.entry_isize); | ||
| 5065 | return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type, | 5085 | return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type, |
| 5066 | size_in_bytes, false); | 5086 | size_in_bytes, depends_on_compile_var); |
| 5067 | } | 5087 | } |
| 5068 | } | 5088 | } |
| 5069 | case BuiltinFnIdAlignof: | 5089 | case BuiltinFnIdAlignof: |
| ... | @@ -5461,8 +5481,11 @@ static TypeTableEntry *analyze_fn_call_with_inline_args(CodeGen *g, ImportTableE | ... | @@ -5461,8 +5481,11 @@ static TypeTableEntry *analyze_fn_call_with_inline_args(CodeGen *g, ImportTableE |
| 5461 | 5481 | ||
| 5462 | ConstExprValue *const_val = &get_resolved_expr(*param_node)->const_val; | 5482 | ConstExprValue *const_val = &get_resolved_expr(*param_node)->const_val; |
| 5463 | if (const_val->ok) { | 5483 | if (const_val->ok) { |
| 5464 | add_local_var(g, generic_param_decl_node, decl_node->owner, child_context, | 5484 | VariableTableEntry *var = add_local_var(g, generic_param_decl_node, decl_node->owner, child_context, |
| 5465 | &generic_param_decl_node->data.param_decl.name, param_type, true, *param_node); | 5485 | &generic_param_decl_node->data.param_decl.name, param_type, true, *param_node); |
| 5486 | // This generic function instance could be called with anything, so when this variable is read it | ||
| 5487 | // needs to know that it depends on compile time variable data. | ||
| 5488 | var->force_depends_on_compile_var = true; | ||
| 5466 | } else { | 5489 | } else { |
| 5467 | add_node_error(g, *param_node, | 5490 | add_node_error(g, *param_node, |
| 5468 | buf_sprintf("unable to evaluate constant expression for inline parameter")); | 5491 | buf_sprintf("unable to evaluate constant expression for inline parameter")); |
| ... | @@ -5552,8 +5575,9 @@ static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *imp | ... | @@ -5552,8 +5575,9 @@ static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *imp |
| 5552 | 5575 | ||
| 5553 | ConstExprValue *const_val = &get_resolved_expr(*param_node)->const_val; | 5576 | ConstExprValue *const_val = &get_resolved_expr(*param_node)->const_val; |
| 5554 | if (const_val->ok) { | 5577 | if (const_val->ok) { |
| 5555 | add_local_var(g, generic_param_decl_node, decl_node->owner, child_context, | 5578 | VariableTableEntry *var = add_local_var(g, generic_param_decl_node, decl_node->owner, child_context, |
| 5556 | &generic_param_decl_node->data.param_decl.name, param_type, true, *param_node); | 5579 | &generic_param_decl_node->data.param_decl.name, param_type, true, *param_node); |
| 5580 | var->force_depends_on_compile_var = true; | ||
| 5557 | } else { | 5581 | } else { |
| 5558 | add_node_error(g, *param_node, buf_sprintf("unable to evaluate constant expression")); | 5582 | add_node_error(g, *param_node, buf_sprintf("unable to evaluate constant expression")); |
| 5559 | 5583 |
src/codegen.cpp+25| ... | @@ -1005,6 +1005,31 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { | ... | @@ -1005,6 +1005,31 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { |
| 1005 | LLVMBuildStore(g->builder, new_len, dest_len_ptr); | 1005 | LLVMBuildStore(g->builder, new_len, dest_len_ptr); |
| 1006 | 1006 | ||
| 1007 | 1007 | ||
| 1008 | return cast_expr->tmp_ptr; | ||
| 1009 | } | ||
| 1010 | case CastOpBytesToSlice: | ||
| 1011 | { | ||
| 1012 | assert(cast_expr->tmp_ptr); | ||
| 1013 | assert(wanted_type->id == TypeTableEntryIdStruct); | ||
| 1014 | assert(wanted_type->data.structure.is_slice); | ||
| 1015 | assert(actual_type->id == TypeTableEntryIdArray); | ||
| 1016 | |||
| 1017 | TypeTableEntry *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry; | ||
| 1018 | TypeTableEntry *wanted_child_type = wanted_pointer_type->data.pointer.child_type; | ||
| 1019 | |||
| 1020 | set_debug_source_node(g, node); | ||
| 1021 | |||
| 1022 | int wanted_ptr_index = wanted_type->data.structure.fields[0].gen_index; | ||
| 1023 | LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, wanted_ptr_index, ""); | ||
| 1024 | LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, expr_val, wanted_pointer_type->type_ref, ""); | ||
| 1025 | LLVMBuildStore(g->builder, src_ptr_casted, dest_ptr_ptr); | ||
| 1026 | |||
| 1027 | int wanted_len_index = wanted_type->data.structure.fields[1].gen_index; | ||
| 1028 | LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, wanted_len_index, ""); | ||
| 1029 | LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, | ||
| 1030 | actual_type->data.array.len / type_size(g, wanted_child_type), false); | ||
| 1031 | LLVMBuildStore(g->builder, len_val, len_ptr); | ||
| 1032 | |||
| 1008 | return cast_expr->tmp_ptr; | 1033 | return cast_expr->tmp_ptr; |
| 1009 | } | 1034 | } |
| 1010 | case CastOpIntToFloat: | 1035 | case CastOpIntToFloat: |
src/eval.cpp+1| ... | @@ -601,6 +601,7 @@ void eval_const_expr_implicit_cast(CastOp cast_op, | ... | @@ -601,6 +601,7 @@ void eval_const_expr_implicit_cast(CastOp cast_op, |
| 601 | case CastOpPtrToInt: | 601 | case CastOpPtrToInt: |
| 602 | case CastOpIntToPtr: | 602 | case CastOpIntToPtr: |
| 603 | case CastOpResizeSlice: | 603 | case CastOpResizeSlice: |
| 604 | case CastOpBytesToSlice: | ||
| 604 | // can't do it | 605 | // can't do it |
| 605 | break; | 606 | break; |
| 606 | case CastOpToUnknownSizeArray: | 607 | case CastOpToUnknownSizeArray: |
src/parser.cpp+2| ... | @@ -3301,6 +3301,8 @@ AstNode *ast_clone_subtree_special(AstNode *old_node, uint32_t *next_node_index, | ... | @@ -3301,6 +3301,8 @@ AstNode *ast_clone_subtree_special(AstNode *old_node, uint32_t *next_node_index, |
| 3301 | case NodeTypeWhileExpr: | 3301 | case NodeTypeWhileExpr: |
| 3302 | clone_subtree_field(&new_node->data.while_expr.condition, old_node->data.while_expr.condition, next_node_index); | 3302 | clone_subtree_field(&new_node->data.while_expr.condition, old_node->data.while_expr.condition, next_node_index); |
| 3303 | clone_subtree_field(&new_node->data.while_expr.body, old_node->data.while_expr.body, next_node_index); | 3303 | clone_subtree_field(&new_node->data.while_expr.body, old_node->data.while_expr.body, next_node_index); |
| 3304 | clone_subtree_field(&new_node->data.while_expr.continue_expr, | ||
| 3305 | old_node->data.while_expr.continue_expr, next_node_index); | ||
| 3304 | break; | 3306 | break; |
| 3305 | case NodeTypeForExpr: | 3307 | case NodeTypeForExpr: |
| 3306 | clone_subtree_field(&new_node->data.for_expr.elem_node, old_node->data.for_expr.elem_node, next_node_index); | 3308 | clone_subtree_field(&new_node->data.for_expr.elem_node, old_node->data.for_expr.elem_node, next_node_index); |
src/zig_llvm.hpp-1| ... | @@ -145,7 +145,6 @@ LLVMZigDISubprogram *LLVMZigCreateFunction(LLVMZigDIBuilder *dibuilder, LLVMZigD | ... | @@ -145,7 +145,6 @@ LLVMZigDISubprogram *LLVMZigCreateFunction(LLVMZigDIBuilder *dibuilder, LLVMZigD |
| 145 | LLVMZigDIType *fn_di_type, bool is_local_to_unit, bool is_definition, unsigned scope_line, | 145 | LLVMZigDIType *fn_di_type, bool is_local_to_unit, bool is_definition, unsigned scope_line, |
| 146 | unsigned flags, bool is_optimized, LLVMZigDISubprogram *decl_subprogram); | 146 | unsigned flags, bool is_optimized, LLVMZigDISubprogram *decl_subprogram); |
| 147 | 147 | ||
| 148 | |||
| 149 | void ZigLLVMFnSetSubprogram(LLVMValueRef fn, LLVMZigDISubprogram *subprogram); | 148 | void ZigLLVMFnSetSubprogram(LLVMValueRef fn, LLVMZigDISubprogram *subprogram); |
| 150 | 149 | ||
| 151 | void LLVMZigDIBuilderFinalize(LLVMZigDIBuilder *dibuilder); | 150 | void LLVMZigDIBuilderFinalize(LLVMZigDIBuilder *dibuilder); |
std/rand.zig+129-65| ... | @@ -1,51 +1,57 @@ | ... | @@ -1,51 +1,57 @@ |
| 1 | // Mersenne Twister | 1 | const assert = @import("debug.zig").assert; |
| 2 | const ARRAY_SIZE = 624; | 2 | |
| 3 | pub const MT19937_32 = MersenneTwister( | ||
| 4 | u32, 624, 397, 31, | ||
| 5 | 0x9908B0DF, | ||
| 6 | 11, 0xFFFFFFFF, | ||
| 7 | 7, 0x9D2C5680, | ||
| 8 | 15, 0xEFC60000, | ||
| 9 | 18, 1812433253); | ||
| 10 | |||
| 11 | pub const MT19937_64 = MersenneTwister( | ||
| 12 | u64, 312, 156, 31, | ||
| 13 | 0xB5026F5AA96619E9, | ||
| 14 | 29, 0x5555555555555555, | ||
| 15 | 17, 0x71D67FFFEDA60000, | ||
| 16 | 37, 0xFFF7EEE000000000, | ||
| 17 | 43, 6364136223846793005); | ||
| 3 | 18 | ||
| 4 | /// Use `init` to initialize this state. | 19 | /// Use `init` to initialize this state. |
| 5 | pub struct Rand { | 20 | pub struct Rand { |
| 6 | array: [ARRAY_SIZE]u32, | 21 | const Rng = if (@sizeof(usize) >= 8) MT19937_64 else MT19937_32; |
| 7 | index: usize, | 22 | |
| 23 | rng: Rng, | ||
| 8 | 24 | ||
| 9 | /// Initialize random state with the given seed. | 25 | /// Initialize random state with the given seed. |
| 10 | #static_eval_enable(false) | 26 | pub fn init(seed: usize) -> Rand { |
| 11 | pub fn init(seed: u32) -> Rand { | ||
| 12 | var r: Rand = undefined; | 27 | var r: Rand = undefined; |
| 13 | r.index = 0; | 28 | r.rng = Rng.init(seed); |
| 14 | r.array[0] = seed; | ||
| 15 | var i : usize = 1; | ||
| 16 | var prev_value: u64w = seed; | ||
| 17 | while (i < ARRAY_SIZE; i += 1) { | ||
| 18 | r.array[i] = @truncate(u32, (prev_value ^ (prev_value << 30)) * 0x6c078965 + u64w(i)); | ||
| 19 | prev_value = r.array[i]; | ||
| 20 | } | ||
| 21 | return r; | 29 | return r; |
| 22 | } | 30 | } |
| 23 | 31 | ||
| 24 | /// Get 32 bits of randomness. | 32 | /// Get an integer with random bits. |
| 25 | pub fn get_u32(r: &Rand) -> u32 { | 33 | pub fn scalar(r: &Rand, inline T: type) -> T { |
| 26 | if (r.index == 0) { | 34 | if (T == usize) { |
| 27 | r.generate_numbers(); | 35 | return r.rng.get(); |
| 36 | } else { | ||
| 37 | var result: T = undefined; | ||
| 38 | r.fill_bytes(([]u8)((&result)[0...@sizeof(T)])); | ||
| 39 | return result; | ||
| 28 | } | 40 | } |
| 29 | |||
| 30 | // temper the number | ||
| 31 | var y : u32 = r.array[r.index]; | ||
| 32 | y ^= y >> 11; | ||
| 33 | y ^= (y >> 7) & 0x9d2c5680; | ||
| 34 | y ^= (y >> 15) & 0xefc60000; | ||
| 35 | y ^= y >> 18; | ||
| 36 | |||
| 37 | r.index = (r.index + 1) % ARRAY_SIZE; | ||
| 38 | return y; | ||
| 39 | } | 41 | } |
| 40 | 42 | ||
| 41 | /// Fill `buf` with randomness. | 43 | /// Fill `buf` with randomness. |
| 42 | pub fn get_bytes(r: &Rand, buf: []u8) { | 44 | pub fn fill_bytes(r: &Rand, buf: []u8) { |
| 43 | var bytes_left = r.get_bytes_aligned(buf); | 45 | var bytes_left = buf.len; |
| 46 | while (bytes_left >= @sizeof(usize)) { | ||
| 47 | *((&usize)(&buf[buf.len - bytes_left])) = r.scalar(usize); | ||
| 48 | bytes_left -= @sizeof(usize); | ||
| 49 | } | ||
| 44 | if (bytes_left > 0) { | 50 | if (bytes_left > 0) { |
| 45 | var rand_val_array : [@sizeof(u32)]u8 = undefined; | 51 | var rand_val_array : [@sizeof(usize)]u8 = undefined; |
| 46 | *((&u32)(&rand_val_array[0])) = r.get_u32(); | 52 | ([]usize)(rand_val_array)[0] = r.scalar(usize); |
| 47 | while (bytes_left > 0) { | 53 | while (bytes_left > 0) { |
| 48 | buf[buf.len - bytes_left] = rand_val_array[@sizeof(u32) - bytes_left]; | 54 | buf[buf.len - bytes_left] = rand_val_array[@sizeof(usize) - bytes_left]; |
| 49 | bytes_left -= 1; | 55 | bytes_left -= 1; |
| 50 | } | 56 | } |
| 51 | } | 57 | } |
| ... | @@ -53,61 +59,119 @@ pub struct Rand { | ... | @@ -53,61 +59,119 @@ pub struct Rand { |
| 53 | 59 | ||
| 54 | /// Get a random unsigned integer with even distribution between `start` | 60 | /// Get a random unsigned integer with even distribution between `start` |
| 55 | /// inclusive and `end` exclusive. | 61 | /// inclusive and `end` exclusive. |
| 56 | pub fn range_u64(r: &Rand, start: u64, end: u64) -> u64 { | 62 | // TODO support signed integers and then rename to "range" |
| 63 | pub fn range_unsigned(r: &Rand, inline T: type, start: T, end: T) -> T { | ||
| 57 | const range = end - start; | 64 | const range = end - start; |
| 58 | const leftover = @max_value(u64) % range; | 65 | const leftover = @max_value(T) % range; |
| 59 | const upper_bound = @max_value(u64) - leftover; | 66 | const upper_bound = @max_value(T) - leftover; |
| 60 | var rand_val_array : [@sizeof(u64)]u8 = undefined; | 67 | var rand_val_array : [@sizeof(T)]u8 = undefined; |
| 61 | 68 | ||
| 62 | while (true) { | 69 | while (true) { |
| 63 | r.get_bytes_aligned(rand_val_array); | 70 | r.fill_bytes(rand_val_array); |
| 64 | const rand_val = *(&u64)(&rand_val_array[0]); | 71 | const rand_val = ([]T)(rand_val_array)[0]; |
| 65 | if (rand_val < upper_bound) { | 72 | if (rand_val < upper_bound) { |
| 66 | return start + (rand_val % range); | 73 | return start + (rand_val % range); |
| 67 | } | 74 | } |
| 68 | } | 75 | } |
| 69 | } | 76 | } |
| 70 | 77 | ||
| 71 | pub fn float32(r: &Rand) -> f32 { | 78 | /// Get a floating point value in the range 0.0..1.0. |
| 72 | const precision = 16777216; | 79 | pub fn float(r: &Rand, inline T: type) -> T { |
| 73 | return f32(r.range_u64(0, precision)) / precision; | 80 | const int_type = @int_type(false, @sizeof(T) * 8, false); |
| 81 | // TODO switch statement for constant values | ||
| 82 | const precision = if (T == f32) { | ||
| 83 | 16777216 | ||
| 84 | } else if (T == f64) { | ||
| 85 | 9007199254740992 | ||
| 86 | } else { | ||
| 87 | @compile_err("unknown floating point type" ++ @type_name(T)) | ||
| 88 | }; | ||
| 89 | return T(r.range_unsigned(int_type, 0, precision)) / T(precision); | ||
| 74 | } | 90 | } |
| 91 | } | ||
| 92 | |||
| 93 | struct MersenneTwister( | ||
| 94 | int: type, n: usize, m: usize, r: int, | ||
| 95 | a: int, | ||
| 96 | u: int, d: int, | ||
| 97 | s: int, b: int, | ||
| 98 | t: int, c: int, | ||
| 99 | l: int, f: int) | ||
| 100 | { | ||
| 101 | const Self = MersenneTwister(int, n, m, r, a, u, d, s, b, t, c, l, f); | ||
| 102 | const intw = @int_type(int.is_signed, int.bit_count, true); | ||
| 103 | |||
| 104 | array: [n]int, | ||
| 105 | index: usize, | ||
| 106 | |||
| 107 | // TODO improve compile time eval code and then allow this function to be executed at compile time. | ||
| 108 | #static_eval_enable(false) | ||
| 109 | pub fn init(seed: int) -> Self { | ||
| 110 | var mt = Self { | ||
| 111 | .index = n, | ||
| 112 | .array = undefined, | ||
| 113 | }; | ||
| 114 | |||
| 115 | var prev_value = seed; | ||
| 116 | mt.array[0] = prev_value; | ||
| 117 | {var i: usize = 1; while (i < n; i += 1) { | ||
| 118 | prev_value = intw(i) + intw(f) * intw(prev_value ^ (prev_value >> (int.bit_count - 2))); | ||
| 119 | mt.array[i] = prev_value; | ||
| 120 | }}; | ||
| 75 | 121 | ||
| 76 | pub fn boolean(r: &Rand) -> bool { | 122 | return mt; |
| 77 | return (r.get_u32() & 0x1) == 1; | ||
| 78 | } | 123 | } |
| 79 | 124 | ||
| 80 | fn generate_numbers(r: &Rand) { | 125 | pub fn get(mt: &Self) -> int { |
| 81 | for (r.array) |item, i| { | 126 | const mag01 = []int{0, a}; |
| 82 | const y : u32 = (item & 0x80000000) + (r.array[(i + 1) % ARRAY_SIZE] & 0x7fffffff); | 127 | const LM: int = (1 << r) - 1; |
| 83 | const untempered : u32 = r.array[(i + 397) % ARRAY_SIZE] ^ (y >> 1); | 128 | const UM = ~LM; |
| 84 | r.array[i] = if ((y % 2) == 0) { | 129 | |
| 85 | untempered | 130 | if (int.bit_count == 64) { |
| 86 | } else { | 131 | assert(LM == 0x7fffffff); |
| 87 | // y is odd | 132 | assert(UM == 0xffffffff80000000); |
| 88 | untempered ^ 0x9908b0df | 133 | } else if (int.bit_count == 32) { |
| 89 | }; | 134 | assert(LM == 0x7fffffff); |
| 135 | assert(UM == 0x80000000); | ||
| 90 | } | 136 | } |
| 91 | } | ||
| 92 | 137 | ||
| 93 | // does not populate the remaining (buf.len % 4) bytes | 138 | if (mt.index >= n) { |
| 94 | fn get_bytes_aligned(r: &Rand, buf: []u8) -> usize { | 139 | var i: usize = 0; |
| 95 | var bytes_left = buf.len; | 140 | |
| 96 | while (bytes_left >= 4) { | 141 | while (i < n - m; i += 1) { |
| 97 | *((&u32)(&buf[buf.len - bytes_left])) = r.get_u32(); | 142 | const x = (mt.array[i] & UM) | (mt.array[i + 1] & LM); |
| 98 | bytes_left -= @sizeof(u32); | 143 | mt.array[i] = mt.array[i + m] ^ (x >> 1) ^ mag01[x & 0x1]; |
| 144 | } | ||
| 145 | |||
| 146 | while (i < n - 1; i += 1) { | ||
| 147 | const x = (mt.array[i] & UM) | (mt.array[i + 1] & LM); | ||
| 148 | mt.array[i] = mt.array[i + m - n] ^ (x >> 1) ^ mag01[x & 0x1]; | ||
| 149 | |||
| 150 | } | ||
| 151 | const x = (mt.array[i] & UM) | (mt.array[0] & LM); | ||
| 152 | mt.array[i] = mt.array[m - 1] ^ (x >> 1) ^ mag01[x & 0x1]; | ||
| 153 | |||
| 154 | mt.index = 0; | ||
| 99 | } | 155 | } |
| 100 | return bytes_left; | ||
| 101 | } | ||
| 102 | 156 | ||
| 157 | var x: intw = mt.array[mt.index]; | ||
| 158 | mt.index += 1; | ||
| 159 | |||
| 160 | x ^= ((x >> u) & d); | ||
| 161 | x ^= ((x << s) & b); | ||
| 162 | x ^= ((x << t) & c); | ||
| 163 | x ^= (x >> l); | ||
| 164 | |||
| 165 | return x; | ||
| 166 | } | ||
| 103 | } | 167 | } |
| 104 | 168 | ||
| 105 | #attribute("test") | 169 | #attribute("test") |
| 106 | fn test_float32() { | 170 | fn test_float32() { |
| 107 | var r = Rand.init(42); | 171 | var r = Rand.init(42); |
| 108 | 172 | ||
| 109 | {var i: i32 = 0; while (i < 1000; i += 1) { | 173 | {var i: usize = 0; while (i < 1000; i += 1) { |
| 110 | const val = r.float32(); | 174 | const val = r.float(f32); |
| 111 | if (!(val >= 0.0)) unreachable{}; | 175 | if (!(val >= 0.0)) unreachable{}; |
| 112 | if (!(val < 1.0)) unreachable{}; | 176 | if (!(val < 1.0)) unreachable{}; |
| 113 | }} | 177 | }} |
test/run_tests.cpp+6| ... | @@ -1427,6 +1427,12 @@ export inline fn foo(x: i32, y: i32) -> i32{ | ... | @@ -1427,6 +1427,12 @@ export inline fn foo(x: i32, y: i32) -> i32{ |
| 1427 | )SOURCE", 1, ".tmp_source.zig:2:1: error: extern functions cannot be inline"); | 1427 | )SOURCE", 1, ".tmp_source.zig:2:1: error: extern functions cannot be inline"); |
| 1428 | */ | 1428 | */ |
| 1429 | 1429 | ||
| 1430 | add_compile_fail_case("convert fixed size array to slice with invalid size", R"SOURCE( | ||
| 1431 | fn f() { | ||
| 1432 | var array: [5]u8 = undefined; | ||
| 1433 | var foo = ([]u32)(array)[0]; | ||
| 1434 | } | ||
| 1435 | )SOURCE", 1, ".tmp_source.zig:4:22: error: unable to convert [5]u8 to []u32: size mismatch"); | ||
| 1430 | } | 1436 | } |
| 1431 | 1437 | ||
| 1432 | ////////////////////////////////////////////////////////////////////////////// | 1438 | ////////////////////////////////////////////////////////////////////////////// |