mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-01 12:18:15 +00:00
Merge remote-tracking branch 'offical/master'
This commit is contained in:
@@ -4,6 +4,7 @@ helper routines.
|
||||
*/
|
||||
package crypto
|
||||
|
||||
import "base:runtime"
|
||||
import "core:mem"
|
||||
|
||||
// compare_constant_time returns 1 iff a and b are equal, 0 otherwise.
|
||||
@@ -58,3 +59,24 @@ rand_bytes :: proc (dst: []byte) {
|
||||
|
||||
_rand_bytes(dst)
|
||||
}
|
||||
|
||||
|
||||
random_generator :: proc() -> runtime.Random_Generator {
|
||||
return {
|
||||
procedure = proc(data: rawptr, mode: runtime.Random_Generator_Mode, p: []byte) {
|
||||
switch mode {
|
||||
case .Read:
|
||||
rand_bytes(p)
|
||||
case .Reset:
|
||||
// do nothing
|
||||
case .Query_Info:
|
||||
if len(p) != size_of(runtime.Random_Generator_Query_Info) {
|
||||
return
|
||||
}
|
||||
info := (^runtime.Random_Generator_Query_Info)(raw_data(p))
|
||||
info^ += {.Uniform, .Cryptographic, .External_Entropy}
|
||||
}
|
||||
},
|
||||
data = nil,
|
||||
}
|
||||
}
|
||||
@@ -56,38 +56,27 @@ CDATA_END :: "]]>"
|
||||
COMMENT_START :: "<!--"
|
||||
COMMENT_END :: "-->"
|
||||
|
||||
/*
|
||||
Default: CDATA and comments are passed through unchanged.
|
||||
*/
|
||||
// Default: CDATA and comments are passed through unchanged.
|
||||
XML_Decode_Option :: enum u8 {
|
||||
/*
|
||||
Do not decode & entities. It decodes by default.
|
||||
If given, overrides `Decode_CDATA`.
|
||||
*/
|
||||
// Do not decode & entities. It decodes by default. If given, overrides `Decode_CDATA`.
|
||||
No_Entity_Decode,
|
||||
|
||||
/*
|
||||
CDATA is unboxed.
|
||||
*/
|
||||
// CDATA is unboxed.
|
||||
Unbox_CDATA,
|
||||
|
||||
/*
|
||||
Unboxed CDATA is decoded as well.
|
||||
Ignored if `.Unbox_CDATA` is not given.
|
||||
*/
|
||||
// Unboxed CDATA is decoded as well. Ignored if `.Unbox_CDATA` is not given.
|
||||
Decode_CDATA,
|
||||
|
||||
/*
|
||||
Comments are stripped.
|
||||
*/
|
||||
// Comments are stripped.
|
||||
Comment_Strip,
|
||||
|
||||
// Normalize whitespace
|
||||
Normalize_Whitespace,
|
||||
}
|
||||
XML_Decode_Options :: bit_set[XML_Decode_Option; u8]
|
||||
|
||||
/*
|
||||
Decode a string that may include SGML/XML/HTML entities.
|
||||
The caller has to free the result.
|
||||
*/
|
||||
// Decode a string that may include SGML/XML/HTML entities.
|
||||
// The caller has to free the result.
|
||||
decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator := context.allocator) -> (decoded: string, err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
@@ -100,14 +89,14 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
|
||||
t := Tokenizer{src=input}
|
||||
in_data := false
|
||||
|
||||
prev: rune = ' '
|
||||
|
||||
loop: for {
|
||||
advance(&t) or_return
|
||||
if t.r < 0 { break loop }
|
||||
|
||||
/*
|
||||
Below here we're never inside a CDATA tag.
|
||||
At most we'll see the start of one, but that doesn't affect the logic.
|
||||
*/
|
||||
// Below here we're never inside a CDATA tag. At most we'll see the start of one,
|
||||
// but that doesn't affect the logic.
|
||||
switch t.r {
|
||||
case '<':
|
||||
/*
|
||||
@@ -126,9 +115,7 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
|
||||
in_data = _handle_xml_special(&t, &builder, options) or_return
|
||||
|
||||
case ']':
|
||||
/*
|
||||
If we're unboxing _and_ decoding CDATA, we'll have to check for the end tag.
|
||||
*/
|
||||
// If we're unboxing _and_ decoding CDATA, we'll have to check for the end tag.
|
||||
if in_data {
|
||||
if t.read_offset + len(CDATA_END) < len(t.src) {
|
||||
if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END {
|
||||
@@ -143,22 +130,16 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
|
||||
|
||||
case:
|
||||
if in_data && .Decode_CDATA not_in options {
|
||||
/*
|
||||
Unboxed, but undecoded.
|
||||
*/
|
||||
// Unboxed, but undecoded.
|
||||
write_rune(&builder, t.r)
|
||||
continue
|
||||
}
|
||||
|
||||
if t.r == '&' {
|
||||
if entity, entity_err := _extract_xml_entity(&t); entity_err != .None {
|
||||
/*
|
||||
We read to the end of the string without closing the entity.
|
||||
Pass through as-is.
|
||||
*/
|
||||
// We read to the end of the string without closing the entity. Pass through as-is.
|
||||
write_string(&builder, entity)
|
||||
} else {
|
||||
|
||||
if .No_Entity_Decode not_in options {
|
||||
if decoded, ok := xml_decode_entity(entity); ok {
|
||||
write_rune(&builder, decoded)
|
||||
@@ -166,19 +147,41 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Literal passthrough because the decode failed or we want entities not decoded.
|
||||
*/
|
||||
// Literal passthrough because the decode failed or we want entities not decoded.
|
||||
write_string(&builder, "&")
|
||||
write_string(&builder, entity)
|
||||
write_string(&builder, ";")
|
||||
}
|
||||
} else {
|
||||
write_rune(&builder, t.r)
|
||||
// Handle AV Normalization: https://www.w3.org/TR/2006/REC-xml11-20060816/#AVNormalize
|
||||
if .Normalize_Whitespace in options {
|
||||
switch t.r {
|
||||
case ' ', '\r', '\n', '\t':
|
||||
if prev != ' ' {
|
||||
write_rune(&builder, ' ')
|
||||
prev = ' '
|
||||
}
|
||||
case:
|
||||
write_rune(&builder, t.r)
|
||||
prev = t.r
|
||||
}
|
||||
} else {
|
||||
// https://www.w3.org/TR/2006/REC-xml11-20060816/#sec-line-ends
|
||||
switch t.r {
|
||||
case '\n', 0x85, 0x2028:
|
||||
write_rune(&builder, '\n')
|
||||
case '\r': // Do nothing until next character
|
||||
case:
|
||||
if prev == '\r' { // Turn a single carriage return into a \n
|
||||
write_rune(&builder, '\n')
|
||||
}
|
||||
write_rune(&builder, t.r)
|
||||
}
|
||||
prev = t.r
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return strings.clone(strings.to_string(builder), allocator), err
|
||||
}
|
||||
|
||||
@@ -253,24 +256,18 @@ xml_decode_entity :: proc(entity: string) -> (decoded: rune, ok: bool) {
|
||||
return rune(val), true
|
||||
|
||||
case:
|
||||
/*
|
||||
Named entity.
|
||||
*/
|
||||
// Named entity.
|
||||
return named_xml_entity_to_rune(entity)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Private XML helper to extract `&<stuff>;` entity.
|
||||
*/
|
||||
// Private XML helper to extract `&<stuff>;` entity.
|
||||
@(private="file")
|
||||
_extract_xml_entity :: proc(t: ^Tokenizer) -> (entity: string, err: Error) {
|
||||
assert(t != nil && t.r == '&')
|
||||
|
||||
/*
|
||||
All of these would be in the ASCII range.
|
||||
Even if one is not, it doesn't matter. All characters we need to compare to extract are.
|
||||
*/
|
||||
// All of these would be in the ASCII range.
|
||||
// Even if one is not, it doesn't matter. All characters we need to compare to extract are.
|
||||
|
||||
length := len(t.src)
|
||||
found := false
|
||||
@@ -292,9 +289,7 @@ _extract_xml_entity :: proc(t: ^Tokenizer) -> (entity: string, err: Error) {
|
||||
return string(t.src[t.offset : t.read_offset]), .Invalid_Entity_Encoding
|
||||
}
|
||||
|
||||
/*
|
||||
Private XML helper for CDATA and comments.
|
||||
*/
|
||||
// Private XML helper for CDATA and comments.
|
||||
@(private="file")
|
||||
_handle_xml_special :: proc(t: ^Tokenizer, builder: ^strings.Builder, options: XML_Decode_Options) -> (in_data: bool, err: Error) {
|
||||
assert(t != nil && t.r == '<')
|
||||
@@ -304,20 +299,14 @@ _handle_xml_special :: proc(t: ^Tokenizer, builder: ^strings.Builder, options: X
|
||||
t.read_offset += len(CDATA_START) - 1
|
||||
|
||||
if .Unbox_CDATA in options && .Decode_CDATA in options {
|
||||
/*
|
||||
We're unboxing _and_ decoding CDATA
|
||||
*/
|
||||
// We're unboxing _and_ decoding CDATA
|
||||
return true, .None
|
||||
}
|
||||
|
||||
/*
|
||||
CDATA is passed through.
|
||||
*/
|
||||
// CDATA is passed through.
|
||||
offset := t.offset
|
||||
|
||||
/*
|
||||
Scan until end of CDATA.
|
||||
*/
|
||||
// Scan until end of CDATA.
|
||||
for {
|
||||
advance(t) or_return
|
||||
if t.r < 0 { return true, .CDATA_Not_Terminated }
|
||||
@@ -341,14 +330,10 @@ _handle_xml_special :: proc(t: ^Tokenizer, builder: ^strings.Builder, options: X
|
||||
|
||||
} else if string(t.src[t.offset:][:len(COMMENT_START)]) == COMMENT_START {
|
||||
t.read_offset += len(COMMENT_START)
|
||||
/*
|
||||
Comment is passed through by default.
|
||||
*/
|
||||
// Comment is passed through by default.
|
||||
offset := t.offset
|
||||
|
||||
/*
|
||||
Scan until end of Comment.
|
||||
*/
|
||||
// Scan until end of Comment.
|
||||
for {
|
||||
advance(t) or_return
|
||||
if t.r < 0 { return true, .Comment_Not_Terminated }
|
||||
|
||||
@@ -218,9 +218,7 @@ scan_identifier :: proc(t: ^Tokenizer) -> string {
|
||||
for is_valid_identifier_rune(t.ch) {
|
||||
advance_rune(t)
|
||||
if t.ch == ':' {
|
||||
/*
|
||||
A namespaced attr can have at most two parts, `namespace:ident`.
|
||||
*/
|
||||
// A namespaced attr can have at most two parts, `namespace:ident`.
|
||||
if namespaced {
|
||||
break
|
||||
}
|
||||
@@ -268,14 +266,10 @@ scan_comment :: proc(t: ^Tokenizer) -> (comment: string, err: Error) {
|
||||
return string(t.src[offset : t.offset - 1]), .None
|
||||
}
|
||||
|
||||
/*
|
||||
Skip CDATA
|
||||
*/
|
||||
// Skip CDATA
|
||||
skip_cdata :: proc(t: ^Tokenizer) -> (err: Error) {
|
||||
if t.read_offset + len(CDATA_START) >= len(t.src) {
|
||||
/*
|
||||
Can't be the start of a CDATA tag.
|
||||
*/
|
||||
// Can't be the start of a CDATA tag.
|
||||
return .None
|
||||
}
|
||||
|
||||
@@ -290,9 +284,7 @@ skip_cdata :: proc(t: ^Tokenizer) -> (err: Error) {
|
||||
return .Premature_EOF
|
||||
}
|
||||
|
||||
/*
|
||||
Scan until the end of a CDATA tag.
|
||||
*/
|
||||
// Scan until the end of a CDATA tag.
|
||||
if t.read_offset + len(CDATA_END) < len(t.src) {
|
||||
if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END {
|
||||
t.read_offset += len(CDATA_END)
|
||||
@@ -319,14 +311,10 @@ scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close
|
||||
case '<':
|
||||
if peek_byte(t) == '!' {
|
||||
if peek_byte(t, 1) == '[' {
|
||||
/*
|
||||
Might be the start of a CDATA tag.
|
||||
*/
|
||||
// Might be the start of a CDATA tag.
|
||||
skip_cdata(t) or_return
|
||||
} else if peek_byte(t, 1) == '-' && peek_byte(t, 2) == '-' {
|
||||
/*
|
||||
Comment start. Eat comment.
|
||||
*/
|
||||
// Comment start. Eat comment.
|
||||
t.read_offset += 3
|
||||
_ = scan_comment(t) or_return
|
||||
}
|
||||
@@ -342,17 +330,13 @@ scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close
|
||||
}
|
||||
|
||||
if t.ch == close {
|
||||
/*
|
||||
If it's not a CDATA or comment, it's the end of this body.
|
||||
*/
|
||||
// If it's not a CDATA or comment, it's the end of this body.
|
||||
break loop
|
||||
}
|
||||
advance_rune(t)
|
||||
}
|
||||
|
||||
/*
|
||||
Strip trailing whitespace.
|
||||
*/
|
||||
// Strip trailing whitespace.
|
||||
lit := string(t.src[offset : t.offset])
|
||||
|
||||
end := len(lit)
|
||||
@@ -369,11 +353,6 @@ scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close
|
||||
if consume_close {
|
||||
advance_rune(t)
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: Handle decoding escape characters and unboxing CDATA.
|
||||
*/
|
||||
|
||||
return lit, err
|
||||
}
|
||||
|
||||
@@ -384,7 +363,7 @@ peek :: proc(t: ^Tokenizer) -> (token: Token) {
|
||||
return token
|
||||
}
|
||||
|
||||
scan :: proc(t: ^Tokenizer) -> Token {
|
||||
scan :: proc(t: ^Tokenizer, multiline_string := false) -> Token {
|
||||
skip_whitespace(t)
|
||||
|
||||
offset := t.offset
|
||||
@@ -418,7 +397,7 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
||||
case '"', '\'':
|
||||
kind = .Invalid
|
||||
|
||||
lit, err = scan_string(t, t.offset, ch, true, false)
|
||||
lit, err = scan_string(t, t.offset, ch, true, multiline_string)
|
||||
if err == .None {
|
||||
kind = .String
|
||||
}
|
||||
@@ -435,4 +414,4 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
||||
lit = string(t.src[offset : t.offset])
|
||||
}
|
||||
return Token{kind, lit, pos}
|
||||
}
|
||||
}
|
||||
@@ -203,9 +203,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
||||
|
||||
doc.elements = make([dynamic]Element, 1024, 1024, allocator)
|
||||
|
||||
// strings.intern_init(&doc.intern, allocator, allocator)
|
||||
|
||||
err = .Unexpected_Token
|
||||
err = .Unexpected_Token
|
||||
element, parent: Element_ID
|
||||
open: Token
|
||||
|
||||
@@ -259,8 +257,8 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
||||
case .Slash:
|
||||
// Empty tag. Close it.
|
||||
expect(t, .Gt) or_return
|
||||
parent = doc.elements[element].parent
|
||||
element = parent
|
||||
parent = doc.elements[element].parent
|
||||
element = parent
|
||||
|
||||
case:
|
||||
error(t, t.offset, "Expected close tag, got: %#v\n", end_token)
|
||||
@@ -276,8 +274,8 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
||||
error(t, t.offset, "Mismatched Closing Tag. Expected %v, got %v\n", doc.elements[element].ident, ident.text)
|
||||
return doc, .Mismatched_Closing_Tag
|
||||
}
|
||||
parent = doc.elements[element].parent
|
||||
element = parent
|
||||
parent = doc.elements[element].parent
|
||||
element = parent
|
||||
|
||||
} else if open.kind == .Exclaim {
|
||||
// <!
|
||||
@@ -463,8 +461,8 @@ validate_options :: proc(options: Options) -> (validated: Options, err: Error) {
|
||||
return validated, .None
|
||||
}
|
||||
|
||||
expect :: proc(t: ^Tokenizer, kind: Token_Kind) -> (tok: Token, err: Error) {
|
||||
tok = scan(t)
|
||||
expect :: proc(t: ^Tokenizer, kind: Token_Kind, multiline_string := false) -> (tok: Token, err: Error) {
|
||||
tok = scan(t, multiline_string=multiline_string)
|
||||
if tok.kind == kind { return tok, .None }
|
||||
|
||||
error(t, t.offset, "Expected \"%v\", got \"%v\".", kind, tok.kind)
|
||||
@@ -480,7 +478,13 @@ parse_attribute :: proc(doc: ^Document) -> (attr: Attribute, offset: int, err: E
|
||||
offset = t.offset - len(key.text)
|
||||
|
||||
_ = expect(t, .Eq) or_return
|
||||
value := expect(t, .String) or_return
|
||||
value := expect(t, .String, multiline_string=true) or_return
|
||||
|
||||
normalized, normalize_err := entity.decode_xml(value.text, {.Normalize_Whitespace}, doc.allocator)
|
||||
if normalize_err == .None {
|
||||
append(&doc.strings_to_free, normalized)
|
||||
value.text = normalized
|
||||
}
|
||||
|
||||
attr.key = key.text
|
||||
attr.val = value.text
|
||||
|
||||
@@ -9,6 +9,7 @@ The verbs:
|
||||
General:
|
||||
%v the value in a default format
|
||||
%#v an expanded format of %v with newlines and indentation
|
||||
%w an Odin-syntax representation of the value
|
||||
%T an Odin-syntax representation of the type of the value
|
||||
%% a literal percent sign; consumes no value
|
||||
{{ a literal open brace; consumes no value
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
//+build ignore
|
||||
package custom_formatter_example
|
||||
import "core:fmt"
|
||||
import "core:io"
|
||||
|
||||
SomeType :: struct {
|
||||
value: int,
|
||||
}
|
||||
|
||||
My_Custom_Base_Type :: distinct u32
|
||||
|
||||
main :: proc() {
|
||||
// Ensure the fmt._user_formatters map is initialized
|
||||
fmt.set_user_formatters(new(map[typeid]fmt.User_Formatter))
|
||||
|
||||
// Register custom formatters for my favorite types
|
||||
err := fmt.register_user_formatter(type_info_of(SomeType).id, SomeType_Formatter)
|
||||
assert(err == .None)
|
||||
err = fmt.register_user_formatter(type_info_of(My_Custom_Base_Type).id, My_Custom_Base_Formatter)
|
||||
assert(err == .None)
|
||||
|
||||
// Use the custom formatters.
|
||||
fmt.printfln("SomeType{{42}}: '%v'", SomeType{42})
|
||||
fmt.printfln("My_Custom_Base_Type(0xdeadbeef): '%v'", My_Custom_Base_Type(0xdeadbeef))
|
||||
}
|
||||
|
||||
SomeType_Formatter :: proc(fi: ^fmt.Info, arg: any, verb: rune) -> bool {
|
||||
m := cast(^SomeType)arg.data
|
||||
switch verb {
|
||||
case 'v', 'd': // We handle `%v` and `%d`
|
||||
fmt.fmt_int(fi, u64(m.value), true, 8 * size_of(SomeType), verb)
|
||||
case:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
My_Custom_Base_Formatter :: proc(fi: ^fmt.Info, arg: any, verb: rune) -> bool {
|
||||
m := cast(^My_Custom_Base_Type)arg.data
|
||||
switch verb {
|
||||
case 'v', 'b':
|
||||
value := u64(m^)
|
||||
for value > 0 {
|
||||
if value & 1 == 1 {
|
||||
io.write_string(fi.writer, "Hellope!", &fi.n)
|
||||
} else {
|
||||
io.write_string(fi.writer, "Hellope?", &fi.n)
|
||||
}
|
||||
value >>= 1
|
||||
}
|
||||
|
||||
case:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
+16
-6
@@ -1726,10 +1726,12 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "", verb: rune = 'v') {
|
||||
|
||||
et := runtime.type_info_base(info.elem)
|
||||
|
||||
if name != "" {
|
||||
io.write_string(fi.writer, name, &fi.n)
|
||||
} else {
|
||||
reflect.write_type(fi.writer, type_info, &fi.n)
|
||||
if verb != 'w' {
|
||||
if name != "" {
|
||||
io.write_string(fi.writer, name, &fi.n)
|
||||
} else {
|
||||
reflect.write_type(fi.writer, type_info, &fi.n)
|
||||
}
|
||||
}
|
||||
io.write_byte(fi.writer, '{', &fi.n)
|
||||
defer io.write_byte(fi.writer, '}', &fi.n)
|
||||
@@ -1746,9 +1748,17 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "", verb: rune = 'v') {
|
||||
}
|
||||
|
||||
if is_enum {
|
||||
enum_name: string
|
||||
if ti_named, is_named := info.elem.variant.(runtime.Type_Info_Named); is_named {
|
||||
enum_name = ti_named.name
|
||||
}
|
||||
for ev, evi in e.values {
|
||||
v := u64(ev)
|
||||
if v == u64(i) {
|
||||
if verb == 'w' {
|
||||
io.write_string(fi.writer, enum_name, &fi.n)
|
||||
io.write_byte(fi.writer, '.', &fi.n)
|
||||
}
|
||||
io.write_string(fi.writer, e.names[evi], &fi.n)
|
||||
commas += 1
|
||||
continue loop
|
||||
@@ -2391,7 +2401,6 @@ fmt_named :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Named)
|
||||
runtime.Type_Info_Dynamic_Array,
|
||||
runtime.Type_Info_Slice,
|
||||
runtime.Type_Info_Struct,
|
||||
runtime.Type_Info_Union,
|
||||
runtime.Type_Info_Enum,
|
||||
runtime.Type_Info_Map,
|
||||
runtime.Type_Info_Bit_Set,
|
||||
@@ -2498,8 +2507,9 @@ fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix
|
||||
}
|
||||
} else {
|
||||
// Printed in Row-Major layout to match text layout
|
||||
row_separator := ", " if verb == 'w' else "; "
|
||||
for row in 0..<info.row_count {
|
||||
if row > 0 { io.write_string(fi.writer, "; ", &fi.n) }
|
||||
if row > 0 { io.write_string(fi.writer, row_separator, &fi.n) }
|
||||
for col in 0..<info.column_count {
|
||||
if col > 0 { io.write_string(fi.writer, ", ", &fi.n) }
|
||||
|
||||
|
||||
@@ -362,11 +362,11 @@ platform_count_lsb :: #force_inline proc(a: $T) -> (count: int)
|
||||
|
||||
count_lsb :: proc { int_count_lsb, platform_count_lsb, }
|
||||
|
||||
int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) {
|
||||
int_random_digit :: proc() -> (res: DIGIT) {
|
||||
when _DIGIT_BITS == 60 { // DIGIT = u64
|
||||
return DIGIT(rnd.uint64(r)) & _MASK
|
||||
return DIGIT(rnd.uint64()) & _MASK
|
||||
} else when _DIGIT_BITS == 28 { // DIGIT = u32
|
||||
return DIGIT(rnd.uint32(r)) & _MASK
|
||||
return DIGIT(rnd.uint32()) & _MASK
|
||||
} else {
|
||||
panic("Unsupported DIGIT size.")
|
||||
}
|
||||
@@ -374,12 +374,12 @@ int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) {
|
||||
return 0 // We shouldn't get here.
|
||||
}
|
||||
|
||||
int_random :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) {
|
||||
int_random :: proc(dest: ^Int, bits: int, allocator := context.allocator) -> (err: Error) {
|
||||
/*
|
||||
Check that `a` is usable.
|
||||
*/
|
||||
assert_if_nil(dest)
|
||||
return #force_inline internal_int_random(dest, bits, r, allocator)
|
||||
return #force_inline internal_int_random(dest, bits, allocator)
|
||||
|
||||
}
|
||||
random :: proc { int_random, }
|
||||
|
||||
@@ -2178,15 +2178,20 @@ internal_int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator
|
||||
}
|
||||
|
||||
/*
|
||||
If not yet iniialized, initialize the `digit` backing with the allocator we were passed.
|
||||
If not yet initialized, initialize the `digit` backing with the allocator we were passed.
|
||||
*/
|
||||
if cap == 0 {
|
||||
a.digit = make([dynamic]DIGIT, needed, allocator)
|
||||
} else if cap != needed {
|
||||
} else if cap < needed {
|
||||
/*
|
||||
`[dynamic]DIGIT` already knows what allocator was used for it, so resize will do the right thing.
|
||||
*/
|
||||
resize(&a.digit, needed)
|
||||
} else if cap > needed {
|
||||
/*
|
||||
Same applies to builtin.shrink here as resize above
|
||||
*/
|
||||
builtin.shrink(&a.digit, needed)
|
||||
}
|
||||
/*
|
||||
Let's see if the allocation/resize worked as expected.
|
||||
@@ -2812,11 +2817,11 @@ internal_platform_count_lsb :: #force_inline proc(a: $T) -> (count: int)
|
||||
|
||||
internal_count_lsb :: proc { internal_int_count_lsb, internal_platform_count_lsb, }
|
||||
|
||||
internal_int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) {
|
||||
internal_int_random_digit :: proc() -> (res: DIGIT) {
|
||||
when _DIGIT_BITS == 60 { // DIGIT = u64
|
||||
return DIGIT(rnd.uint64(r)) & _MASK
|
||||
return DIGIT(rnd.uint64()) & _MASK
|
||||
} else when _DIGIT_BITS == 28 { // DIGIT = u32
|
||||
return DIGIT(rnd.uint32(r)) & _MASK
|
||||
return DIGIT(rnd.uint32()) & _MASK
|
||||
} else {
|
||||
panic("Unsupported DIGIT size.")
|
||||
}
|
||||
@@ -2824,7 +2829,7 @@ internal_int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) {
|
||||
return 0 // We shouldn't get here.
|
||||
}
|
||||
|
||||
internal_int_random :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) {
|
||||
internal_int_random :: proc(dest: ^Int, bits: int, allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
bits := bits
|
||||
@@ -2841,7 +2846,7 @@ internal_int_random :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator
|
||||
#force_inline internal_grow(dest, digits) or_return
|
||||
|
||||
for i := 0; i < digits; i += 1 {
|
||||
dest.digit[i] = int_random_digit(r) & _MASK
|
||||
dest.digit[i] = int_random_digit() & _MASK
|
||||
}
|
||||
if bits > 0 {
|
||||
dest.digit[digits - 1] &= ((1 << uint(bits)) - 1)
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
|
||||
package math_big
|
||||
|
||||
import rnd "core:math/rand"
|
||||
|
||||
/*
|
||||
Determines if an Integer is divisible by one of the _PRIME_TABLE primes.
|
||||
Returns true if it is, false if not.
|
||||
@@ -315,7 +313,7 @@ internal_int_prime_miller_rabin :: proc(a, b: ^Int, allocator := context.allocat
|
||||
|
||||
Assumes `a` not to be `nil` and to have been initialized.
|
||||
*/
|
||||
internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_rabin_only := USE_MILLER_RABIN_ONLY, r: ^rnd.Rand = nil, allocator := context.allocator) -> (is_prime: bool, err: Error) {
|
||||
internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_rabin_only := USE_MILLER_RABIN_ONLY, allocator := context.allocator) -> (is_prime: bool, err: Error) {
|
||||
context.allocator = allocator
|
||||
miller_rabin_trials := miller_rabin_trials
|
||||
|
||||
@@ -461,7 +459,7 @@ internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_ra
|
||||
for ix := 0; ix < miller_rabin_trials; ix += 1 {
|
||||
|
||||
// rand() guarantees the first digit to be non-zero
|
||||
internal_random(b, _DIGIT_TYPE_BITS, r) or_return
|
||||
internal_random(b, _DIGIT_TYPE_BITS) or_return
|
||||
|
||||
// Reduce digit before casting because DIGIT might be bigger than
|
||||
// an unsigned int and "mask" on the other side is most probably not.
|
||||
@@ -1183,7 +1181,7 @@ internal_int_prime_next_prime :: proc(a: ^Int, trials: int, bbs_style: bool, all
|
||||
|
||||
This is possibly the mother of all prime generation functions, muahahahahaha!
|
||||
*/
|
||||
internal_random_prime :: proc(a: ^Int, size_in_bits: int, trials: int, flags := Primality_Flags{}, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) {
|
||||
internal_random_prime :: proc(a: ^Int, size_in_bits: int, trials: int, flags := Primality_Flags{}, allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
flags := flags
|
||||
trials := trials
|
||||
|
||||
@@ -8,12 +8,12 @@ float32_uniform :: float32_range
|
||||
// Triangular Distribution
|
||||
// See: http://wikipedia.org/wiki/Triangular_distribution
|
||||
@(require_results)
|
||||
float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64), r: ^Rand = nil) -> f64 {
|
||||
float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64)) -> f64 {
|
||||
if hi-lo == 0 {
|
||||
return lo
|
||||
}
|
||||
lo, hi := lo, hi
|
||||
u := float64(r)
|
||||
u := float64()
|
||||
c := f64(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
|
||||
if u > c {
|
||||
u = 1-u
|
||||
@@ -26,12 +26,12 @@ float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64), r: ^Rand = nil) -> f64
|
||||
// Triangular Distribution
|
||||
// See: http://wikipedia.org/wiki/Triangular_distribution
|
||||
@(require_results)
|
||||
float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32), r: ^Rand = nil) -> f32 {
|
||||
float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32)) -> f32 {
|
||||
if hi-lo == 0 {
|
||||
return lo
|
||||
}
|
||||
lo, hi := lo, hi
|
||||
u := float32(r)
|
||||
u := float32()
|
||||
c := f32(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
|
||||
if u > c {
|
||||
u = 1-u
|
||||
@@ -44,25 +44,25 @@ float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32), r: ^Rand = nil) -> f32
|
||||
|
||||
// Normal/Gaussian Distribution
|
||||
@(require_results)
|
||||
float64_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 {
|
||||
return norm_float64(r) * stddev + mean
|
||||
float64_normal :: proc(mean, stddev: f64) -> f64 {
|
||||
return norm_float64() * stddev + mean
|
||||
}
|
||||
// Normal/Gaussian Distribution
|
||||
@(require_results)
|
||||
float32_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_normal(f64(mean), f64(stddev), r))
|
||||
float32_normal :: proc(mean, stddev: f32) -> f32 {
|
||||
return f32(float64_normal(f64(mean), f64(stddev)))
|
||||
}
|
||||
|
||||
|
||||
// Log Normal Distribution
|
||||
@(require_results)
|
||||
float64_log_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 {
|
||||
return math.exp(float64_normal(mean, stddev, r))
|
||||
float64_log_normal :: proc(mean, stddev: f64) -> f64 {
|
||||
return math.exp(float64_normal(mean, stddev))
|
||||
}
|
||||
// Log Normal Distribution
|
||||
@(require_results)
|
||||
float32_log_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_log_normal(f64(mean), f64(stddev), r))
|
||||
float32_log_normal :: proc(mean, stddev: f32) -> f32 {
|
||||
return f32(float64_log_normal(f64(mean), f64(stddev)))
|
||||
}
|
||||
|
||||
|
||||
@@ -72,8 +72,8 @@ float32_log_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 {
|
||||
// 0 to positive infinity if lambda > 0
|
||||
// negative infinity to 0 if lambda <= 0
|
||||
@(require_results)
|
||||
float64_exponential :: proc(lambda: f64, r: ^Rand = nil) -> f64 {
|
||||
return - math.ln(1 - float64(r)) / lambda
|
||||
float64_exponential :: proc(lambda: f64) -> f64 {
|
||||
return - math.ln(1 - float64()) / lambda
|
||||
}
|
||||
// Exponential Distribution
|
||||
// `lambda` is 1.0/(desired mean). It should be non-zero.
|
||||
@@ -81,8 +81,8 @@ float64_exponential :: proc(lambda: f64, r: ^Rand = nil) -> f64 {
|
||||
// 0 to positive infinity if lambda > 0
|
||||
// negative infinity to 0 if lambda <= 0
|
||||
@(require_results)
|
||||
float32_exponential :: proc(lambda: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_exponential(f64(lambda), r))
|
||||
float32_exponential :: proc(lambda: f32) -> f32 {
|
||||
return f32(float64_exponential(f64(lambda)))
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ float32_exponential :: proc(lambda: f32, r: ^Rand = nil) -> f32 {
|
||||
//
|
||||
// mean is alpha*beta, variance is math.pow(alpha*beta, 2)
|
||||
@(require_results)
|
||||
float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
|
||||
float64_gamma :: proc(alpha, beta: f64) -> f64 {
|
||||
if alpha <= 0 || beta <= 0 {
|
||||
panic(#procedure + ": alpha and beta must be > 0.0")
|
||||
}
|
||||
@@ -112,11 +112,11 @@ float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
|
||||
bbb := alpha - LOG4
|
||||
ccc := alpha + ainv
|
||||
for {
|
||||
u1 := float64(r)
|
||||
u1 := float64()
|
||||
if !(1e-7 < u1 && u1 < 0.9999999) {
|
||||
continue
|
||||
}
|
||||
u2 := 1 - float64(r)
|
||||
u2 := 1 - float64()
|
||||
v := math.ln(u1 / (1 - u1)) / ainv
|
||||
x := alpha * math.exp(v)
|
||||
z := u1 * u1 * u2
|
||||
@@ -127,12 +127,12 @@ float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
|
||||
}
|
||||
case alpha == 1:
|
||||
// float64_exponential(1/beta)
|
||||
return -math.ln(1 - float64(r)) * beta
|
||||
return -math.ln(1 - float64()) * beta
|
||||
case:
|
||||
// ALGORITHM GS of Statistical Computing - Kennedy & Gentle
|
||||
x: f64
|
||||
for {
|
||||
u := float64(r)
|
||||
u := float64()
|
||||
b := (math.e + alpha) / math.e
|
||||
p := b * u
|
||||
if p <= 1 {
|
||||
@@ -140,7 +140,7 @@ float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
|
||||
} else {
|
||||
x = -math.ln((b - p) / alpha)
|
||||
}
|
||||
u1 := float64(r)
|
||||
u1 := float64()
|
||||
if p > 1 {
|
||||
if u1 <= math.pow(x, alpha-1) {
|
||||
break
|
||||
@@ -162,8 +162,8 @@ float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
|
||||
//
|
||||
// mean is alpha*beta, variance is math.pow(alpha*beta, 2)
|
||||
@(require_results)
|
||||
float32_gamma :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_gamma(f64(alpha), f64(beta), r))
|
||||
float32_gamma :: proc(alpha, beta: f32) -> f32 {
|
||||
return f32(float64_gamma(f64(alpha), f64(beta)))
|
||||
}
|
||||
|
||||
|
||||
@@ -173,14 +173,14 @@ float32_gamma :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
|
||||
//
|
||||
// Return values range between 0 and 1
|
||||
@(require_results)
|
||||
float64_beta :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
|
||||
float64_beta :: proc(alpha, beta: f64) -> f64 {
|
||||
if alpha <= 0 || beta <= 0 {
|
||||
panic(#procedure + ": alpha and beta must be > 0.0")
|
||||
}
|
||||
// Knuth Vol 2 Ed 3 pg 134 "the beta distribution"
|
||||
y := float64_gamma(alpha, 1.0, r)
|
||||
y := float64_gamma(alpha, 1.0)
|
||||
if y != 0 {
|
||||
return y / (y + float64_gamma(beta, 1.0, r))
|
||||
return y / (y + float64_gamma(beta, 1.0))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -190,35 +190,35 @@ float64_beta :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
|
||||
//
|
||||
// Return values range between 0 and 1
|
||||
@(require_results)
|
||||
float32_beta :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_beta(f64(alpha), f64(beta), r))
|
||||
float32_beta :: proc(alpha, beta: f32) -> f32 {
|
||||
return f32(float64_beta(f64(alpha), f64(beta)))
|
||||
}
|
||||
|
||||
|
||||
// Pareto distribution, `alpha` is the shape parameter.
|
||||
// https://wikipedia.org/wiki/Pareto_distribution
|
||||
@(require_results)
|
||||
float64_pareto :: proc(alpha: f64, r: ^Rand = nil) -> f64 {
|
||||
return math.pow(1 - float64(r), -1.0 / alpha)
|
||||
float64_pareto :: proc(alpha: f64) -> f64 {
|
||||
return math.pow(1 - float64(), -1.0 / alpha)
|
||||
}
|
||||
// Pareto distribution, `alpha` is the shape parameter.
|
||||
// https://wikipedia.org/wiki/Pareto_distribution
|
||||
@(require_results)
|
||||
float32_pareto :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_pareto(f64(alpha), r))
|
||||
float32_pareto :: proc(alpha, beta: f32) -> f32 {
|
||||
return f32(float64_pareto(f64(alpha)))
|
||||
}
|
||||
|
||||
|
||||
// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
|
||||
@(require_results)
|
||||
float64_weibull :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
|
||||
u := 1 - float64(r)
|
||||
float64_weibull :: proc(alpha, beta: f64) -> f64 {
|
||||
u := 1 - float64()
|
||||
return alpha * math.pow(-math.ln(u), 1.0/beta)
|
||||
}
|
||||
// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
|
||||
@(require_results)
|
||||
float32_weibull :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_weibull(f64(alpha), f64(beta), r))
|
||||
float32_weibull :: proc(alpha, beta: f32) -> f32 {
|
||||
return f32(float64_weibull(f64(alpha), f64(beta)))
|
||||
}
|
||||
|
||||
|
||||
@@ -227,23 +227,23 @@ float32_weibull :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
|
||||
// `kappa` is the concentration parameter which must be >= 0
|
||||
// When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
|
||||
@(require_results)
|
||||
float64_von_mises :: proc(mean_angle, kappa: f64, r: ^Rand = nil) -> f64 {
|
||||
float64_von_mises :: proc(mean_angle, kappa: f64) -> f64 {
|
||||
// Fisher, N.I., "Statistical Analysis of Circular Data", Cambridge University Press, 1993.
|
||||
|
||||
mu := mean_angle
|
||||
if kappa <= 1e-6 {
|
||||
return math.TAU * float64(r)
|
||||
return math.TAU * float64()
|
||||
}
|
||||
|
||||
s := 0.5 / kappa
|
||||
t := s + math.sqrt(1 + s*s)
|
||||
z: f64
|
||||
for {
|
||||
u1 := float64(r)
|
||||
u1 := float64()
|
||||
z = math.cos(math.TAU * 0.5 * u1)
|
||||
|
||||
d := z / (t + z)
|
||||
u2 := float64(r)
|
||||
u2 := float64()
|
||||
if u2 < 1 - d*d || u2 <= (1-d)*math.exp(d) {
|
||||
break
|
||||
}
|
||||
@@ -251,7 +251,7 @@ float64_von_mises :: proc(mean_angle, kappa: f64, r: ^Rand = nil) -> f64 {
|
||||
|
||||
q := 1.0 / t
|
||||
f := (q + z) / (1 + q*z)
|
||||
u3 := float64(r)
|
||||
u3 := float64()
|
||||
if u3 > 0.5 {
|
||||
return math.mod(mu + math.acos(f), math.TAU)
|
||||
} else {
|
||||
@@ -263,57 +263,57 @@ float64_von_mises :: proc(mean_angle, kappa: f64, r: ^Rand = nil) -> f64 {
|
||||
// `kappa` is the concentration parameter which must be >= 0
|
||||
// When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
|
||||
@(require_results)
|
||||
float32_von_mises :: proc(mean_angle, kappa: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_von_mises(f64(mean_angle), f64(kappa), r))
|
||||
float32_von_mises :: proc(mean_angle, kappa: f32) -> f32 {
|
||||
return f32(float64_von_mises(f64(mean_angle), f64(kappa)))
|
||||
}
|
||||
|
||||
|
||||
// Cauchy-Lorentz Distribution
|
||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||
@(require_results)
|
||||
float64_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 {
|
||||
float64_cauchy_lorentz :: proc(x_0, gamma: f64) -> f64 {
|
||||
assert(gamma > 0)
|
||||
|
||||
// Calculated from the inverse CDF
|
||||
|
||||
return math.tan(math.PI * (float64(r) - 0.5))*gamma + x_0
|
||||
return math.tan(math.PI * (float64() - 0.5))*gamma + x_0
|
||||
}
|
||||
// Cauchy-Lorentz Distribution
|
||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||
@(require_results)
|
||||
float32_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma), r))
|
||||
float32_cauchy_lorentz :: proc(x_0, gamma: f32) -> f32 {
|
||||
return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma)))
|
||||
}
|
||||
|
||||
|
||||
// Log Cauchy-Lorentz Distribution
|
||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||
@(require_results)
|
||||
float64_log_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 {
|
||||
float64_log_cauchy_lorentz :: proc(x_0, gamma: f64) -> f64 {
|
||||
assert(gamma > 0)
|
||||
return math.exp(math.tan(math.PI * (float64(r) - 0.5))*gamma + x_0)
|
||||
return math.exp(math.tan(math.PI * (float64() - 0.5))*gamma + x_0)
|
||||
}
|
||||
// Log Cauchy-Lorentz Distribution
|
||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||
@(require_results)
|
||||
float32_log_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_log_cauchy_lorentz(f64(x_0), f64(gamma), r))
|
||||
float32_log_cauchy_lorentz :: proc(x_0, gamma: f32) -> f32 {
|
||||
return f32(float64_log_cauchy_lorentz(f64(x_0), f64(gamma)))
|
||||
}
|
||||
|
||||
|
||||
// Laplace Distribution
|
||||
// `b` is the scale where `b` > 0
|
||||
@(require_results)
|
||||
float64_laplace :: proc(mean, b: f64, r: ^Rand = nil) -> f64 {
|
||||
float64_laplace :: proc(mean, b: f64) -> f64 {
|
||||
assert(b > 0)
|
||||
p := float64(r)-0.5
|
||||
p := float64()-0.5
|
||||
return -math.sign(p)*math.ln(1 - 2*abs(p))*b + mean
|
||||
}
|
||||
// Laplace Distribution
|
||||
// `b` is the scale where `b` > 0
|
||||
@(require_results)
|
||||
float32_laplace :: proc(mean, b: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_laplace(f64(mean), f64(b), r))
|
||||
float32_laplace :: proc(mean, b: f32) -> f32 {
|
||||
return f32(float64_laplace(f64(mean), f64(b)))
|
||||
}
|
||||
|
||||
|
||||
@@ -321,18 +321,18 @@ float32_laplace :: proc(mean, b: f32, r: ^Rand = nil) -> f32 {
|
||||
// `eta` is the shape, `b` is the scale
|
||||
// Both `eta` and `b` must be > 0
|
||||
@(require_results)
|
||||
float64_gompertz :: proc(eta, b: f64, r: ^Rand = nil) -> f64 {
|
||||
float64_gompertz :: proc(eta, b: f64) -> f64 {
|
||||
if eta <= 0 || b <= 0 {
|
||||
panic(#procedure + ": eta and b must be > 0.0")
|
||||
}
|
||||
|
||||
p := float64(r)
|
||||
p := float64()
|
||||
return math.ln(1 - math.ln(1 - p)/eta)/b
|
||||
}
|
||||
// Gompertz Distribution
|
||||
// `eta` is the shape, `b` is the scale
|
||||
// Both `eta` and `b` must be > 0
|
||||
@(require_results)
|
||||
float32_gompertz :: proc(eta, b: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_gompertz(f64(eta), f64(b), r))
|
||||
float32_gompertz :: proc(eta, b: f32) -> f32 {
|
||||
return f32(float64_gompertz(f64(eta), f64(b)))
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import "core:math"
|
||||
// https://www.jstatsoft.org/article/view/v005i08 [web page]
|
||||
//
|
||||
@(require_results)
|
||||
exp_float64 :: proc(r: ^Rand = nil) -> f64 {
|
||||
exp_float64 :: proc() -> f64 {
|
||||
re :: 7.69711747013104972
|
||||
|
||||
@(static, rodata)
|
||||
@@ -199,16 +199,16 @@ exp_float64 :: proc(r: ^Rand = nil) -> f64 {
|
||||
}
|
||||
|
||||
for {
|
||||
j := uint32(r)
|
||||
j := uint32()
|
||||
i := j & 0xFF
|
||||
x := f64(j) * f64(we[i])
|
||||
if j < ke[i] {
|
||||
return x
|
||||
}
|
||||
if i == 0 {
|
||||
return re - math.ln(float64(r))
|
||||
return re - math.ln(float64())
|
||||
}
|
||||
if fe[i]+f32(float64(r))*(fe[i-1]-fe[i]) < f32(math.exp(-x)) {
|
||||
if fe[i]+f32(float64())*(fe[i-1]-fe[i]) < f32(math.exp(-x)) {
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import "core:math"
|
||||
// https://www.jstatsoft.org/article/view/v005i08 [web page]
|
||||
//
|
||||
@(require_results)
|
||||
norm_float64 :: proc(r: ^Rand = nil) -> f64 {
|
||||
norm_float64 :: proc() -> f64 {
|
||||
rn :: 3.442619855899
|
||||
|
||||
@(static, rodata)
|
||||
@@ -115,15 +115,8 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 {
|
||||
0.008624485, 0.005548995, 0.0026696292,
|
||||
}
|
||||
|
||||
r := r
|
||||
if r == nil {
|
||||
// NOTE(bill, 2020-09-07): Do this so that people can
|
||||
// enforce the global random state if necessary with `nil`
|
||||
r = &global_rand
|
||||
}
|
||||
|
||||
for {
|
||||
j := i32(uint32(r))
|
||||
j := i32(uint32())
|
||||
i := j & 0x7f
|
||||
x := f64(j) * f64(wn[i])
|
||||
if u32(abs(j)) < kn[i] {
|
||||
@@ -133,15 +126,15 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 {
|
||||
|
||||
if i == 0 {
|
||||
for {
|
||||
x = -math.ln(float64(r)) * (1.0/ rn)
|
||||
y := -math.ln(float64(r))
|
||||
x = -math.ln(float64()) * (1.0/ rn)
|
||||
y := -math.ln(float64())
|
||||
if y+y >= x*x {
|
||||
break
|
||||
}
|
||||
}
|
||||
return j > 0 ? rn + x : -rn - x
|
||||
}
|
||||
if fn[i]+f32(float64(r))*(fn[i-1]-fn[i]) < f32(math.exp(-0.5*x*x)) {
|
||||
if fn[i]+f32(float64())*(fn[i-1]-fn[i]) < f32(math.exp(-0.5*x*x)) {
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
+63
-252
@@ -5,22 +5,22 @@ Package core:math/rand implements various random number generators
|
||||
package rand
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:crypto"
|
||||
import "base:runtime"
|
||||
import "core:math"
|
||||
import "core:mem"
|
||||
|
||||
Rand :: struct {
|
||||
state: u64,
|
||||
inc: u64,
|
||||
is_system: bool,
|
||||
Default_Random_State :: runtime.Default_Random_State
|
||||
default_random_generator :: runtime.default_random_generator
|
||||
|
||||
create :: proc(seed: u64) -> (state: Default_Random_State) {
|
||||
seed := seed
|
||||
runtime.default_random_generator(&state)
|
||||
runtime.default_random_generator_proc(&state, .Reset, ([^]byte)(&seed)[:size_of(seed)])
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
global_rand := create(u64(intrinsics.read_cycle_counter()))
|
||||
|
||||
/*
|
||||
Sets the seed used by the global random number generator.
|
||||
Reset the seed used by the context.random_generator.
|
||||
|
||||
Inputs:
|
||||
- seed: The seed value
|
||||
@@ -37,139 +37,46 @@ Example:
|
||||
Possible Output:
|
||||
|
||||
10
|
||||
|
||||
*/
|
||||
@(deprecated="Prefer `rand.reset`")
|
||||
set_global_seed :: proc(seed: u64) {
|
||||
init(&global_rand, seed)
|
||||
runtime.random_generator_reset_u64(context.random_generator, seed)
|
||||
}
|
||||
|
||||
/*
|
||||
Creates a new random number generator.
|
||||
Reset the seed used by the context.random_generator.
|
||||
|
||||
Inputs:
|
||||
- seed: The seed value to create the random number generator with
|
||||
|
||||
Returns:
|
||||
- res: The created random number generator
|
||||
- seed: The seed value
|
||||
|
||||
Example:
|
||||
import "core:math/rand"
|
||||
import "core:fmt"
|
||||
|
||||
create_example :: proc() {
|
||||
my_rand := rand.create(1)
|
||||
fmt.println(rand.uint64(&my_rand))
|
||||
set_global_seed_example :: proc() {
|
||||
rand.set_global_seed(1)
|
||||
fmt.println(rand.uint64())
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
|
||||
10
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
create :: proc(seed: u64) -> (res: Rand) {
|
||||
r: Rand
|
||||
init(&r, seed)
|
||||
return r
|
||||
reset :: proc(seed: u64) {
|
||||
runtime.random_generator_reset_u64(context.random_generator, seed)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Initialises a random number generator.
|
||||
|
||||
Inputs:
|
||||
- r: The random number generator to initialise
|
||||
- seed: The seed value to initialise this random number generator
|
||||
|
||||
Example:
|
||||
import "core:math/rand"
|
||||
import "core:fmt"
|
||||
|
||||
init_example :: proc() {
|
||||
my_rand: rand.Rand
|
||||
rand.init(&my_rand, 1)
|
||||
fmt.println(rand.uint64(&my_rand))
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
|
||||
10
|
||||
|
||||
*/
|
||||
init :: proc(r: ^Rand, seed: u64) {
|
||||
r.state = 0
|
||||
r.inc = (seed << 1) | 1
|
||||
_random_u64(r)
|
||||
r.state += seed
|
||||
_random_u64(r)
|
||||
}
|
||||
|
||||
/*
|
||||
Initialises a random number generator to use the system random number generator.
|
||||
The system random number generator is platform specific, and not supported
|
||||
on all targets.
|
||||
|
||||
Inputs:
|
||||
- r: The random number generator to use the system random number generator
|
||||
|
||||
WARNING: Panics if the system random number generator is not supported.
|
||||
Support can be determined via the `core:crypto.HAS_RAND_BYTES` constant.
|
||||
|
||||
Example:
|
||||
import "core:crypto"
|
||||
import "core:math/rand"
|
||||
import "core:fmt"
|
||||
|
||||
init_as_system_example :: proc() {
|
||||
my_rand: rand.Rand
|
||||
switch crypto.HAS_RAND_BYTES {
|
||||
case true:
|
||||
rand.init_as_system(&my_rand)
|
||||
fmt.println(rand.uint64(&my_rand))
|
||||
case false:
|
||||
fmt.println("system random not supported!")
|
||||
}
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
|
||||
10
|
||||
|
||||
*/
|
||||
init_as_system :: proc(r: ^Rand) {
|
||||
if !crypto.HAS_RAND_BYTES {
|
||||
panic(#procedure + " is not supported on this platform yet")
|
||||
}
|
||||
r.state = 0
|
||||
r.inc = 0
|
||||
r.is_system = true
|
||||
}
|
||||
|
||||
@(private)
|
||||
_random_u64 :: proc(r: ^Rand) -> u64 {
|
||||
r := r
|
||||
switch {
|
||||
case r == nil:
|
||||
r = &global_rand
|
||||
case r.is_system:
|
||||
value: u64
|
||||
crypto.rand_bytes((cast([^]u8)&value)[:size_of(u64)])
|
||||
return value
|
||||
}
|
||||
|
||||
old_state := r.state
|
||||
r.state = old_state * 6364136223846793005 + (r.inc|1)
|
||||
xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081
|
||||
rot := (old_state >> 59)
|
||||
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63))
|
||||
_random_u64 :: proc() -> (res: u64) {
|
||||
ok := runtime.random_generator_read_ptr(context.random_generator, &res, size_of(res))
|
||||
assert(ok, "uninitialized context.random_generator")
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Generates a random 32 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
|
||||
|
||||
Inputs:
|
||||
- r: The random number generator to use, or nil for the global generator
|
||||
|
||||
Returns:
|
||||
- val: A random unsigned 32 bit value
|
||||
|
||||
@@ -178,11 +85,7 @@ Example:
|
||||
import "core:fmt"
|
||||
|
||||
uint32_example :: proc() {
|
||||
// Using the global random number generator
|
||||
fmt.println(rand.uint32())
|
||||
// Using local random number generator
|
||||
my_rand := rand.create(1)
|
||||
fmt.println(rand.uint32(&my_rand))
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
@@ -192,14 +95,11 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
uint32 :: proc(r: ^Rand = nil) -> (val: u32) { return u32(_random_u64(r)) }
|
||||
uint32 :: proc() -> (val: u32) { return u32(_random_u64()) }
|
||||
|
||||
/*
|
||||
Generates a random 64 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
|
||||
|
||||
Inputs:
|
||||
- r: The random number generator to use, or nil for the global generator
|
||||
|
||||
Returns:
|
||||
- val: A random unsigned 64 bit value
|
||||
|
||||
@@ -208,11 +108,7 @@ Example:
|
||||
import "core:fmt"
|
||||
|
||||
uint64_example :: proc() {
|
||||
// Using the global random number generator
|
||||
fmt.println(rand.uint64())
|
||||
// Using local random number generator
|
||||
my_rand := rand.create(1)
|
||||
fmt.println(rand.uint64(&my_rand))
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
@@ -222,14 +118,11 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
uint64 :: proc(r: ^Rand = nil) -> (val: u64) { return _random_u64(r) }
|
||||
uint64 :: proc() -> (val: u64) { return _random_u64() }
|
||||
|
||||
/*
|
||||
Generates a random 128 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
|
||||
|
||||
Inputs:
|
||||
- r: The random number generator to use, or nil for the global generator
|
||||
|
||||
Returns:
|
||||
- val: A random unsigned 128 bit value
|
||||
|
||||
@@ -238,11 +131,7 @@ Example:
|
||||
import "core:fmt"
|
||||
|
||||
uint128_example :: proc() {
|
||||
// Using the global random number generator
|
||||
fmt.println(rand.uint128())
|
||||
// Using local random number generator
|
||||
my_rand := rand.create(1)
|
||||
fmt.println(rand.uint128(&my_rand))
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
@@ -252,9 +141,9 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
uint128 :: proc(r: ^Rand = nil) -> (val: u128) {
|
||||
a := u128(_random_u64(r))
|
||||
b := u128(_random_u64(r))
|
||||
uint128 :: proc() -> (val: u128) {
|
||||
a := u128(_random_u64())
|
||||
b := u128(_random_u64())
|
||||
return (a<<64) | b
|
||||
}
|
||||
|
||||
@@ -262,9 +151,6 @@ uint128 :: proc(r: ^Rand = nil) -> (val: u128) {
|
||||
Generates a random 31 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
|
||||
The sign bit will always be set to 0, thus all generated numbers will be positive.
|
||||
|
||||
Inputs:
|
||||
- r: The random number generator to use, or nil for the global generator
|
||||
|
||||
Returns:
|
||||
- val: A random 31 bit value
|
||||
|
||||
@@ -273,11 +159,7 @@ Example:
|
||||
import "core:fmt"
|
||||
|
||||
int31_example :: proc() {
|
||||
// Using the global random number generator
|
||||
fmt.println(rand.int31())
|
||||
// Using local random number generator
|
||||
my_rand := rand.create(1)
|
||||
fmt.println(rand.int31(&my_rand))
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
@@ -286,15 +168,12 @@ Possible Output:
|
||||
389
|
||||
|
||||
*/
|
||||
@(require_results) int31 :: proc(r: ^Rand = nil) -> (val: i32) { return i32(uint32(r) << 1 >> 1) }
|
||||
@(require_results) int31 :: proc() -> (val: i32) { return i32(uint32() << 1 >> 1) }
|
||||
|
||||
/*
|
||||
Generates a random 63 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
|
||||
The sign bit will always be set to 0, thus all generated numbers will be positive.
|
||||
|
||||
Inputs:
|
||||
- r: The random number generator to use, or nil for the global generator
|
||||
|
||||
Returns:
|
||||
- val: A random 63 bit value
|
||||
|
||||
@@ -303,11 +182,7 @@ Example:
|
||||
import "core:fmt"
|
||||
|
||||
int63_example :: proc() {
|
||||
// Using the global random number generator
|
||||
fmt.println(rand.int63())
|
||||
// Using local random number generator
|
||||
my_rand := rand.create(1)
|
||||
fmt.println(rand.int63(&my_rand))
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
@@ -316,15 +191,12 @@ Possible Output:
|
||||
389
|
||||
|
||||
*/
|
||||
@(require_results) int63 :: proc(r: ^Rand = nil) -> (val: i64) { return i64(uint64(r) << 1 >> 1) }
|
||||
@(require_results) int63 :: proc() -> (val: i64) { return i64(uint64() << 1 >> 1) }
|
||||
|
||||
/*
|
||||
Generates a random 127 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
|
||||
The sign bit will always be set to 0, thus all generated numbers will be positive.
|
||||
|
||||
Inputs:
|
||||
- r: The random number generator to use, or nil for the global generator
|
||||
|
||||
Returns:
|
||||
- val: A random 127 bit value
|
||||
|
||||
@@ -333,11 +205,7 @@ Example:
|
||||
import "core:fmt"
|
||||
|
||||
int127_example :: proc() {
|
||||
// Using the global random number generator
|
||||
fmt.println(rand.int127())
|
||||
// Using local random number generator
|
||||
my_rand := rand.create(1)
|
||||
fmt.println(rand.int127(&my_rand))
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
@@ -346,14 +214,13 @@ Possible Output:
|
||||
389
|
||||
|
||||
*/
|
||||
@(require_results) int127 :: proc(r: ^Rand = nil) -> (val: i128) { return i128(uint128(r) << 1 >> 1) }
|
||||
@(require_results) int127 :: proc() -> (val: i128) { return i128(uint128() << 1 >> 1) }
|
||||
|
||||
/*
|
||||
Generates a random 31 bit value in the range `[0, n)` using the provided random number generator. If no generator is provided the global random number generator will be used.
|
||||
|
||||
Inputs:
|
||||
- n: The upper bound of the generated number, this value is exclusive
|
||||
- r: The random number generator to use, or nil for the global generator
|
||||
|
||||
Returns:
|
||||
- val: A random 31 bit value in the range `[0, n)`
|
||||
@@ -365,11 +232,7 @@ Example:
|
||||
import "core:fmt"
|
||||
|
||||
int31_max_example :: proc() {
|
||||
// Using the global random number generator
|
||||
fmt.println(rand.int31_max(16))
|
||||
// Using local random number generator
|
||||
my_rand := rand.create(1)
|
||||
fmt.println(rand.int31_max(1024, &my_rand))
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
@@ -379,17 +242,17 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
int31_max :: proc(n: i32, r: ^Rand = nil) -> (val: i32) {
|
||||
int31_max :: proc(n: i32) -> (val: i32) {
|
||||
if n <= 0 {
|
||||
panic("Invalid argument to int31_max")
|
||||
}
|
||||
if n&(n-1) == 0 {
|
||||
return int31(r) & (n-1)
|
||||
return int31() & (n-1)
|
||||
}
|
||||
max := i32((1<<31) - 1 - (1<<31)%u32(n))
|
||||
v := int31(r)
|
||||
v := int31()
|
||||
for v > max {
|
||||
v = int31(r)
|
||||
v = int31()
|
||||
}
|
||||
return v % n
|
||||
}
|
||||
@@ -399,7 +262,6 @@ Generates a random 63 bit value in the range `[0, n)` using the provided random
|
||||
|
||||
Inputs:
|
||||
- n: The upper bound of the generated number, this value is exclusive
|
||||
- r: The random number generator to use, or nil for the global generator
|
||||
|
||||
Returns:
|
||||
- val: A random 63 bit value in the range `[0, n)`
|
||||
@@ -411,11 +273,7 @@ Example:
|
||||
import "core:fmt"
|
||||
|
||||
int63_max_example :: proc() {
|
||||
// Using the global random number generator
|
||||
fmt.println(rand.int63_max(16))
|
||||
// Using local random number generator
|
||||
my_rand := rand.create(1)
|
||||
fmt.println(rand.int63_max(1024, &my_rand))
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
@@ -425,17 +283,17 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
int63_max :: proc(n: i64, r: ^Rand = nil) -> (val: i64) {
|
||||
int63_max :: proc(n: i64) -> (val: i64) {
|
||||
if n <= 0 {
|
||||
panic("Invalid argument to int63_max")
|
||||
}
|
||||
if n&(n-1) == 0 {
|
||||
return int63(r) & (n-1)
|
||||
return int63() & (n-1)
|
||||
}
|
||||
max := i64((1<<63) - 1 - (1<<63)%u64(n))
|
||||
v := int63(r)
|
||||
v := int63()
|
||||
for v > max {
|
||||
v = int63(r)
|
||||
v = int63()
|
||||
}
|
||||
return v % n
|
||||
}
|
||||
@@ -445,7 +303,6 @@ Generates a random 127 bit value in the range `[0, n)` using the provided random
|
||||
|
||||
Inputs:
|
||||
- n: The upper bound of the generated number, this value is exclusive
|
||||
- r: The random number generator to use, or nil for the global generator
|
||||
|
||||
Returns:
|
||||
- val: A random 127 bit value in the range `[0, n)`
|
||||
@@ -457,11 +314,7 @@ Example:
|
||||
import "core:fmt"
|
||||
|
||||
int127_max_example :: proc() {
|
||||
// Using the global random number generator
|
||||
fmt.println(rand.int127_max(16))
|
||||
// Using local random number generator
|
||||
my_rand := rand.create(1)
|
||||
fmt.println(rand.int127_max(1024, &my_rand))
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
@@ -471,17 +324,17 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
int127_max :: proc(n: i128, r: ^Rand = nil) -> (val: i128) {
|
||||
int127_max :: proc(n: i128) -> (val: i128) {
|
||||
if n <= 0 {
|
||||
panic("Invalid argument to int127_max")
|
||||
}
|
||||
if n&(n-1) == 0 {
|
||||
return int127(r) & (n-1)
|
||||
return int127() & (n-1)
|
||||
}
|
||||
max := i128((1<<127) - 1 - (1<<127)%u128(n))
|
||||
v := int127(r)
|
||||
v := int127()
|
||||
for v > max {
|
||||
v = int127(r)
|
||||
v = int127()
|
||||
}
|
||||
return v % n
|
||||
}
|
||||
@@ -491,7 +344,6 @@ Generates a random integer value in the range `[0, n)` using the provided random
|
||||
|
||||
Inputs:
|
||||
- n: The upper bound of the generated number, this value is exclusive
|
||||
- r: The random number generator to use, or nil for the global generator
|
||||
|
||||
Returns:
|
||||
- val: A random integer value in the range `[0, n)`
|
||||
@@ -503,11 +355,7 @@ Example:
|
||||
import "core:fmt"
|
||||
|
||||
int_max_example :: proc() {
|
||||
// Using the global random number generator
|
||||
fmt.println(rand.int_max(16))
|
||||
// Using local random number generator
|
||||
my_rand := rand.create(1)
|
||||
fmt.println(rand.int_max(1024, &my_rand))
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
@@ -517,23 +365,20 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
int_max :: proc(n: int, r: ^Rand = nil) -> (val: int) {
|
||||
int_max :: proc(n: int) -> (val: int) {
|
||||
if n <= 0 {
|
||||
panic("Invalid argument to int_max")
|
||||
}
|
||||
when size_of(int) == 4 {
|
||||
return int(int31_max(i32(n), r))
|
||||
return int(int31_max(i32(n)))
|
||||
} else {
|
||||
return int(int63_max(i64(n), r))
|
||||
return int(int63_max(i64(n)))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Generates a random double floating point value in the range `[0, 1)` using the provided random number generator. If no generator is provided the global random number generator will be used.
|
||||
|
||||
Inputs:
|
||||
- r: The random number generator to use, or nil for the global generator
|
||||
|
||||
Returns:
|
||||
- val: A random double floating point value in the range `[0, 1)`
|
||||
|
||||
@@ -542,11 +387,7 @@ Example:
|
||||
import "core:fmt"
|
||||
|
||||
float64_example :: proc() {
|
||||
// Using the global random number generator
|
||||
fmt.println(rand.float64())
|
||||
// Using local random number generator
|
||||
my_rand := rand.create(1)
|
||||
fmt.println(rand.float64(&my_rand))
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
@@ -555,14 +396,11 @@ Possible Output:
|
||||
0.511
|
||||
|
||||
*/
|
||||
@(require_results) float64 :: proc(r: ^Rand = nil) -> (val: f64) { return f64(int63_max(1<<53, r)) / (1 << 53) }
|
||||
@(require_results) float64 :: proc() -> (val: f64) { return f64(int63_max(1<<53)) / (1 << 53) }
|
||||
|
||||
/*
|
||||
Generates a random single floating point value in the range `[0, 1)` using the provided random number generator. If no generator is provided the global random number generator will be used.
|
||||
|
||||
Inputs:
|
||||
- r: The random number generator to use, or nil for the global generator
|
||||
|
||||
Returns:
|
||||
- val: A random single floating point value in the range `[0, 1)`
|
||||
|
||||
@@ -571,11 +409,7 @@ Example:
|
||||
import "core:fmt"
|
||||
|
||||
float32_example :: proc() {
|
||||
// Using the global random number generator
|
||||
fmt.println(rand.float32())
|
||||
// Using local random number generator
|
||||
my_rand := rand.create(1)
|
||||
fmt.println(rand.float32(&my_rand))
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
@@ -584,7 +418,7 @@ Possible Output:
|
||||
0.511
|
||||
|
||||
*/
|
||||
@(require_results) float32 :: proc(r: ^Rand = nil) -> (val: f32) { return f32(int31_max(1<<24, r)) / (1 << 24) }
|
||||
@(require_results) float32 :: proc() -> (val: f32) { return f32(int31_max(1<<24)) / (1 << 24) }
|
||||
|
||||
/*
|
||||
Generates a random double floating point value in the range `[low, high)` using the provided random number generator. If no generator is provided the global random number generator will be used.
|
||||
@@ -594,7 +428,6 @@ WARNING: Panics if `high < low`
|
||||
Inputs:
|
||||
- low: The lower bounds of the value, this value is inclusive
|
||||
- high: The upper bounds of the value, this value is exclusive
|
||||
- r: The random number generator to use, or nil for the global generator
|
||||
|
||||
Returns:
|
||||
- val: A random double floating point value in the range [low, high)
|
||||
@@ -604,11 +437,7 @@ Example:
|
||||
import "core:fmt"
|
||||
|
||||
float64_range_example :: proc() {
|
||||
// Using the global random number generator
|
||||
fmt.println(rand.float64_range(-10, 300))
|
||||
// Using local random number generator
|
||||
my_rand := rand.create(1)
|
||||
fmt.println(rand.float64_range(600, 900, &my_rand))
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
@@ -617,9 +446,9 @@ Possible Output:
|
||||
673.130
|
||||
|
||||
*/
|
||||
@(require_results) float64_range :: proc(low, high: f64, r: ^Rand = nil) -> (val: f64) {
|
||||
@(require_results) float64_range :: proc(low, high: f64) -> (val: f64) {
|
||||
assert(low <= high, "low must be lower than or equal to high")
|
||||
val = (high-low)*float64(r) + low
|
||||
val = (high-low)*float64() + low
|
||||
if val >= high {
|
||||
val = max(low, high * (1 - math.F64_EPSILON))
|
||||
}
|
||||
@@ -632,7 +461,6 @@ Generates a random single floating point value in the range `[low, high)` using
|
||||
Inputs:
|
||||
- low: The lower bounds of the value, this value is inclusive
|
||||
- high: The upper bounds of the value, this value is exclusive
|
||||
- r: The random number generator to use, or nil for the global generator
|
||||
|
||||
Returns:
|
||||
- val: A random single floating point value in the range [low, high)
|
||||
@@ -644,11 +472,7 @@ Example:
|
||||
import "core:fmt"
|
||||
|
||||
float32_range_example :: proc() {
|
||||
// Using the global random number generator
|
||||
fmt.println(rand.float32_range(-10, 300))
|
||||
// Using local random number generator
|
||||
my_rand := rand.create(1)
|
||||
fmt.println(rand.float32_range(600, 900, &my_rand))
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
@@ -657,9 +481,9 @@ Possible Output:
|
||||
673.130
|
||||
|
||||
*/
|
||||
@(require_results) float32_range :: proc(low, high: f32, r: ^Rand = nil) -> (val: f32) {
|
||||
@(require_results) float32_range :: proc(low, high: f32) -> (val: f32) {
|
||||
assert(low <= high, "low must be lower than or equal to high")
|
||||
val = (high-low)*float32(r) + low
|
||||
val = (high-low)*float32() + low
|
||||
if val >= high {
|
||||
val = max(low, high * (1 - math.F32_EPSILON))
|
||||
}
|
||||
@@ -672,7 +496,6 @@ Due to floating point precision there is no guarantee if the upper and lower bou
|
||||
|
||||
Inputs:
|
||||
- p: The byte slice to fill
|
||||
- r: The random number generator to use, or nil for the global generator
|
||||
|
||||
Returns:
|
||||
- n: The number of bytes generated
|
||||
@@ -682,7 +505,6 @@ Example:
|
||||
import "core:fmt"
|
||||
|
||||
read_example :: proc() {
|
||||
// Using the global random number generator
|
||||
data: [8]byte
|
||||
n := rand.read(data[:])
|
||||
fmt.println(n)
|
||||
@@ -696,12 +518,12 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) {
|
||||
read :: proc(p: []byte) -> (n: int) {
|
||||
pos := i8(0)
|
||||
val := i64(0)
|
||||
for n = 0; n < len(p); n += 1 {
|
||||
if pos == 0 {
|
||||
val = int63(r)
|
||||
val = int63()
|
||||
pos = 7
|
||||
}
|
||||
p[n] = byte(val)
|
||||
@@ -718,7 +540,6 @@ Creates a slice of `int` filled with random values using the provided random num
|
||||
|
||||
Inputs:
|
||||
- n: The size of the created slice
|
||||
- r: The random number generator to use, or nil for the global generator
|
||||
- allocator: (default: context.allocator)
|
||||
|
||||
Returns:
|
||||
@@ -731,16 +552,10 @@ Example:
|
||||
import "core:fmt"
|
||||
|
||||
perm_example :: proc() -> (err: mem.Allocator_Error) {
|
||||
// Using the global random number generator and using the context allocator
|
||||
data := rand.perm(4) or_return
|
||||
fmt.println(data)
|
||||
defer delete(data, context.allocator)
|
||||
|
||||
// Using local random number generator and temp allocator
|
||||
my_rand := rand.create(1)
|
||||
data_tmp := rand.perm(4, &my_rand, context.temp_allocator) or_return
|
||||
fmt.println(data_tmp)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -751,10 +566,10 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
perm :: proc(n: int, r: ^Rand = nil, allocator := context.allocator) -> (res: []int, err: mem.Allocator_Error) #optional_allocator_error {
|
||||
perm :: proc(n: int, allocator := context.allocator) -> (res: []int, err: mem.Allocator_Error) #optional_allocator_error {
|
||||
m := make([]int, n, allocator) or_return
|
||||
for i := 0; i < n; i += 1 {
|
||||
j := int_max(i+1, r)
|
||||
j := int_max(i+1)
|
||||
m[i] = m[j]
|
||||
m[j] = i
|
||||
}
|
||||
@@ -766,14 +581,12 @@ Randomizes the ordering of elements for the provided slice. If no generator is p
|
||||
|
||||
Inputs:
|
||||
- array: The slice to randomize
|
||||
- r: The random number generator to use, or nil for the global generator
|
||||
|
||||
Example:
|
||||
import "core:math/rand"
|
||||
import "core:fmt"
|
||||
|
||||
shuffle_example :: proc() {
|
||||
// Using the global random number generator
|
||||
data: [4]int = { 1, 2, 3, 4 }
|
||||
fmt.println(data) // the contents are in order
|
||||
rand.shuffle(data[:])
|
||||
@@ -786,14 +599,14 @@ Possible Output:
|
||||
[2, 4, 3, 1]
|
||||
|
||||
*/
|
||||
shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) {
|
||||
shuffle :: proc(array: $T/[]$E) {
|
||||
n := i64(len(array))
|
||||
if n < 2 {
|
||||
return
|
||||
}
|
||||
|
||||
for i := i64(n - 1); i > 0; i -= 1 {
|
||||
j := int63_max(i + 1, r)
|
||||
j := int63_max(i + 1)
|
||||
array[i], array[j] = array[j], array[i]
|
||||
}
|
||||
}
|
||||
@@ -803,7 +616,6 @@ Returns a random element from the provided slice. If no generator is provided th
|
||||
|
||||
Inputs:
|
||||
- array: The slice to choose an element from
|
||||
- r: The random number generator to use, or nil for the global generator
|
||||
|
||||
Returns:
|
||||
- res: A random element from `array`
|
||||
@@ -813,7 +625,6 @@ Example:
|
||||
import "core:fmt"
|
||||
|
||||
choice_example :: proc() {
|
||||
// Using the global random number generator
|
||||
data: [4]int = { 1, 2, 3, 4 }
|
||||
fmt.println(rand.choice(data[:]))
|
||||
fmt.println(rand.choice(data[:]))
|
||||
@@ -830,17 +641,17 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
choice :: proc(array: $T/[]$E, r: ^Rand = nil) -> (res: E) {
|
||||
choice :: proc(array: $T/[]$E) -> (res: E) {
|
||||
n := i64(len(array))
|
||||
if n < 1 {
|
||||
return E{}
|
||||
}
|
||||
return array[int63_max(n, r)]
|
||||
return array[int63_max(n)]
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
choice_enum :: proc($T: typeid, r: ^Rand = nil) -> T
|
||||
choice_enum :: proc($T: typeid) -> T
|
||||
where
|
||||
intrinsics.type_is_enum(T),
|
||||
size_of(T) <= 8,
|
||||
@@ -848,11 +659,11 @@ choice_enum :: proc($T: typeid, r: ^Rand = nil) -> T
|
||||
{
|
||||
when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
|
||||
u64(max(T)) > u64(max(i64)) {
|
||||
i := uint64(r) % u64(len(T))
|
||||
i := uint64() % u64(len(T))
|
||||
i += u64(min(T))
|
||||
return T(i)
|
||||
} else {
|
||||
i := int63_max(i64(len(T)), r)
|
||||
i := int63_max(i64(len(T)))
|
||||
i += i64(min(T))
|
||||
return T(i)
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ binary_search_by :: proc(array: $A/[]$T, key: T, f: proc(T, T) -> Ordering) -> (
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
equal :: proc(a, b: $T/[]$E) -> bool where intrinsics.type_is_comparable(E) {
|
||||
equal :: proc(a, b: $T/[]$E) -> bool where intrinsics.type_is_comparable(E) #no_bounds_check {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
@@ -736,4 +736,4 @@ bitset_to_enum_slice_with_make :: proc(bs: $T, $E: typeid, allocator := context.
|
||||
return bitset_to_enum_slice(buf, bs)
|
||||
}
|
||||
|
||||
bitset_to_enum_slice :: proc{bitset_to_enum_slice_with_make, bitset_to_enum_slice_with_buffer}
|
||||
bitset_to_enum_slice :: proc{bitset_to_enum_slice_with_make, bitset_to_enum_slice_with_buffer}
|
||||
|
||||
@@ -476,10 +476,7 @@ select_raw :: proc "odin" (recvs: []^Raw_Chan, sends: []^Raw_Chan, send_msgs: []
|
||||
return
|
||||
}
|
||||
|
||||
r: ^rand.Rand = nil
|
||||
|
||||
|
||||
select_idx = rand.int_max(count, r) if count > 0 else 0
|
||||
select_idx = rand.int_max(count) if count > 0 else 0
|
||||
|
||||
sel := candidates[select_idx]
|
||||
if sel.is_recv {
|
||||
|
||||
Regular → Executable
+72
-109
@@ -64,6 +64,7 @@ foreign kernel32 {
|
||||
RemoveVectoredContinueHandler :: proc(Handle: LPVOID) -> DWORD ---
|
||||
RaiseException :: proc(dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD, lpArguments: ^ULONG_PTR) -> ! ---
|
||||
|
||||
SetUnhandledExceptionFilter :: proc(lpTopLevelExceptionFilter: LPTOP_LEVEL_EXCEPTION_FILTER) -> LPTOP_LEVEL_EXCEPTION_FILTER ---
|
||||
|
||||
CreateHardLinkW :: proc(lpSymlinkFileName: LPCWSTR,
|
||||
lpTargetFileName: LPCWSTR,
|
||||
@@ -464,6 +465,8 @@ foreign kernel32 {
|
||||
GetHandleInformation :: proc(hObject: HANDLE, lpdwFlags: ^DWORD) -> BOOL ---
|
||||
|
||||
RtlCaptureStackBackTrace :: proc(FramesToSkip: ULONG, FramesToCapture: ULONG, BackTrace: [^]PVOID, BackTraceHash: PULONG) -> USHORT ---
|
||||
|
||||
GetSystemPowerStatus :: proc(lpSystemPowerStatus: ^SYSTEM_POWER_STATUS) -> BOOL ---
|
||||
}
|
||||
|
||||
DEBUG_PROCESS :: 0x00000001
|
||||
@@ -1009,31 +1012,9 @@ foreign kernel32 {
|
||||
HandlerRoutine :: proc "system" (dwCtrlType: DWORD) -> BOOL
|
||||
PHANDLER_ROUTINE :: HandlerRoutine
|
||||
|
||||
// NOTE(Jeroen, 2024-06-13): As Odin now supports bit_fields, we no longer need
|
||||
// a helper procedure. `init_dcb_with_config` and `get_dcb_config` have been removed.
|
||||
|
||||
|
||||
|
||||
DCB_Config :: struct {
|
||||
fParity: bool,
|
||||
fOutxCtsFlow: bool,
|
||||
fOutxDsrFlow: bool,
|
||||
fDtrControl: DTR_Control,
|
||||
fDsrSensitivity: bool,
|
||||
fTXContinueOnXoff: bool,
|
||||
fOutX: bool,
|
||||
fInX: bool,
|
||||
fErrorChar: bool,
|
||||
fNull: bool,
|
||||
fRtsControl: RTS_Control,
|
||||
fAbortOnError: bool,
|
||||
BaudRate: DWORD,
|
||||
ByteSize: BYTE,
|
||||
Parity: Parity,
|
||||
StopBits: Stop_Bits,
|
||||
XonChar: byte,
|
||||
XoffChar: byte,
|
||||
ErrorChar: byte,
|
||||
EvtChar: byte,
|
||||
}
|
||||
DTR_Control :: enum byte {
|
||||
Disable = 0,
|
||||
Enable = 1,
|
||||
@@ -1058,92 +1039,35 @@ Stop_Bits :: enum byte {
|
||||
Two = 2,
|
||||
}
|
||||
|
||||
// A helper procedure to set the values of a DCB structure.
|
||||
init_dcb_with_config :: proc "contextless" (dcb: ^DCB, config: DCB_Config) {
|
||||
out: u32
|
||||
|
||||
// NOTE(tetra, 2022-09-21): On both Clang 14 on Windows, and MSVC, the bits in the bitfield
|
||||
// appear to be defined from LSB to MSB order.
|
||||
// i.e: `fBinary` (the first bitfield in the C source) is the LSB in the `settings` u32.
|
||||
|
||||
out |= u32(1) << 0 // fBinary must always be true on Windows.
|
||||
|
||||
out |= u32(config.fParity) << 1
|
||||
out |= u32(config.fOutxCtsFlow) << 2
|
||||
out |= u32(config.fOutxDsrFlow) << 3
|
||||
|
||||
out |= u32(config.fDtrControl) << 4
|
||||
|
||||
out |= u32(config.fDsrSensitivity) << 6
|
||||
out |= u32(config.fTXContinueOnXoff) << 7
|
||||
out |= u32(config.fOutX) << 8
|
||||
out |= u32(config.fInX) << 9
|
||||
out |= u32(config.fErrorChar) << 10
|
||||
out |= u32(config.fNull) << 11
|
||||
|
||||
out |= u32(config.fRtsControl) << 12
|
||||
|
||||
out |= u32(config.fAbortOnError) << 14
|
||||
|
||||
dcb.settings = out
|
||||
|
||||
dcb.BaudRate = config.BaudRate
|
||||
dcb.ByteSize = config.ByteSize
|
||||
dcb.Parity = config.Parity
|
||||
dcb.StopBits = config.StopBits
|
||||
dcb.XonChar = config.XonChar
|
||||
dcb.XoffChar = config.XoffChar
|
||||
dcb.ErrorChar = config.ErrorChar
|
||||
dcb.EvtChar = config.EvtChar
|
||||
|
||||
dcb.DCBlength = size_of(DCB)
|
||||
}
|
||||
get_dcb_config :: proc "contextless" (dcb: DCB) -> (config: DCB_Config) {
|
||||
config.fParity = bool((dcb.settings >> 1) & 0x01)
|
||||
config.fOutxCtsFlow = bool((dcb.settings >> 2) & 0x01)
|
||||
config.fOutxDsrFlow = bool((dcb.settings >> 3) & 0x01)
|
||||
|
||||
config.fDtrControl = DTR_Control((dcb.settings >> 4) & 0x02)
|
||||
|
||||
config.fDsrSensitivity = bool((dcb.settings >> 6) & 0x01)
|
||||
config.fTXContinueOnXoff = bool((dcb.settings >> 7) & 0x01)
|
||||
config.fOutX = bool((dcb.settings >> 8) & 0x01)
|
||||
config.fInX = bool((dcb.settings >> 9) & 0x01)
|
||||
config.fErrorChar = bool((dcb.settings >> 10) & 0x01)
|
||||
config.fNull = bool((dcb.settings >> 11) & 0x01)
|
||||
|
||||
config.fRtsControl = RTS_Control((dcb.settings >> 12) & 0x02)
|
||||
|
||||
config.fAbortOnError = bool((dcb.settings >> 14) & 0x01)
|
||||
|
||||
config.BaudRate = dcb.BaudRate
|
||||
config.ByteSize = dcb.ByteSize
|
||||
config.Parity = dcb.Parity
|
||||
config.StopBits = dcb.StopBits
|
||||
config.XonChar = dcb.XonChar
|
||||
config.XoffChar = dcb.XoffChar
|
||||
config.ErrorChar = dcb.ErrorChar
|
||||
config.EvtChar = dcb.EvtChar
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// NOTE(tetra): See get_dcb_config() and init_dcb_with_config() for help with initializing this.
|
||||
DCB :: struct {
|
||||
DCBlength: DWORD, // NOTE(tetra): Must be set to size_of(DCB).
|
||||
BaudRate: DWORD,
|
||||
settings: u32, // NOTE(tetra): These are bitfields in the C struct.
|
||||
wReserved: WORD,
|
||||
XOnLim: WORD,
|
||||
XOffLim: WORD,
|
||||
ByteSize: BYTE,
|
||||
Parity: Parity,
|
||||
StopBits: Stop_Bits,
|
||||
XonChar: byte,
|
||||
XoffChar: byte,
|
||||
ErrorChar: byte,
|
||||
EofChar: byte,
|
||||
EvtChar: byte,
|
||||
DCBlength: DWORD,
|
||||
BaudRate: DWORD,
|
||||
using _: bit_field DWORD {
|
||||
fBinary: bool | 1,
|
||||
fParity: bool | 1,
|
||||
fOutxCtsFlow: bool | 1,
|
||||
fOutxDsrFlow: bool | 1,
|
||||
fDtrControl: DTR_Control | 2,
|
||||
fDsrSensitivity: bool | 1,
|
||||
fTXContinueOnXoff: bool | 1,
|
||||
fOutX: bool | 1,
|
||||
fInX: bool | 1,
|
||||
fErrorChar: bool | 1,
|
||||
fNull: bool | 1,
|
||||
fRtsControl: RTS_Control | 2,
|
||||
fAbortOnError: bool | 1,
|
||||
},
|
||||
wReserved: WORD,
|
||||
XOnLim: WORD,
|
||||
XOffLim: WORD,
|
||||
ByteSize: BYTE,
|
||||
Parity: Parity,
|
||||
StopBits: Stop_Bits,
|
||||
XonChar: byte,
|
||||
XoffChar: byte,
|
||||
ErrorChar: byte,
|
||||
EofChar: byte,
|
||||
EvtChar: byte,
|
||||
wReserved1: WORD,
|
||||
}
|
||||
|
||||
@@ -1153,6 +1077,19 @@ foreign kernel32 {
|
||||
SetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
|
||||
}
|
||||
|
||||
COMMTIMEOUTS :: struct {
|
||||
ReadIntervalTimeout: DWORD,
|
||||
ReadTotalTimeoutMultiplier: DWORD,
|
||||
ReadTotalTimeoutConstant: DWORD,
|
||||
WriteTotalTimeoutMultiplier: DWORD,
|
||||
WriteTotalTimeoutConstant: DWORD,
|
||||
}
|
||||
|
||||
@(default_calling_convention="system")
|
||||
foreign kernel32 {
|
||||
GetCommTimeouts :: proc(handle: HANDLE, timeouts: ^COMMTIMEOUTS) -> BOOL ---
|
||||
SetCommTimeouts :: proc(handle: HANDLE, timeouts: ^COMMTIMEOUTS) -> BOOL ---
|
||||
}
|
||||
|
||||
LPFIBER_START_ROUTINE :: #type proc "system" (lpFiberParameter: LPVOID)
|
||||
|
||||
@@ -1210,6 +1147,30 @@ SYSTEM_LOGICAL_PROCESSOR_INFORMATION :: struct {
|
||||
DummyUnion: DUMMYUNIONNAME_u,
|
||||
}
|
||||
|
||||
SYSTEM_POWER_STATUS :: struct {
|
||||
ACLineStatus: AC_Line_Status,
|
||||
BatteryFlag: Battery_Flags,
|
||||
BatteryLifePercent: BYTE,
|
||||
SystemStatusFlag: BYTE,
|
||||
BatteryLifeTime: DWORD,
|
||||
BatteryFullLifeTime: DWORD,
|
||||
}
|
||||
|
||||
AC_Line_Status :: enum BYTE {
|
||||
Offline = 0,
|
||||
Online = 1,
|
||||
Unknown = 255,
|
||||
}
|
||||
|
||||
Battery_Flag :: enum BYTE {
|
||||
High = 0,
|
||||
Low = 1,
|
||||
Critical = 2,
|
||||
Charging = 3,
|
||||
No_Battery = 7,
|
||||
}
|
||||
Battery_Flags :: bit_set[Battery_Flag; BYTE]
|
||||
|
||||
/* Global Memory Flags */
|
||||
GMEM_FIXED :: 0x0000
|
||||
GMEM_MOVEABLE :: 0x0002
|
||||
@@ -1228,3 +1189,5 @@ GMEM_INVALID_HANDLE :: 0x8000
|
||||
|
||||
GHND :: (GMEM_MOVEABLE | GMEM_ZEROINIT)
|
||||
GPTR :: (GMEM_FIXED | GMEM_ZEROINIT)
|
||||
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER :: PVECTORED_EXCEPTION_HANDLER
|
||||
|
||||
@@ -34,6 +34,7 @@ HGDIOBJ :: distinct HANDLE
|
||||
HBITMAP :: distinct HANDLE
|
||||
HGLOBAL :: distinct HANDLE
|
||||
HHOOK :: distinct HANDLE
|
||||
HWINEVENTHOOK :: distinct HANDLE
|
||||
HKEY :: distinct HANDLE
|
||||
HDESK :: distinct HANDLE
|
||||
HFONT :: distinct HANDLE
|
||||
@@ -703,6 +704,14 @@ WNDPROC :: #type proc "system" (HWND, UINT, WPARAM, LPARAM) -> LRESULT
|
||||
|
||||
HOOKPROC :: #type proc "system" (code: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT
|
||||
|
||||
WINEVENTPROC :: #type proc "system" (
|
||||
hWinEventHook: HWINEVENTHOOK,
|
||||
event: DWORD,
|
||||
hwnd: HWND,
|
||||
idObject, idChild: LONG,
|
||||
idEventThread, dwmsEventTime: DWORD,
|
||||
)
|
||||
|
||||
CWPRETSTRUCT :: struct {
|
||||
lResult: LRESULT,
|
||||
lParam: LPARAM,
|
||||
|
||||
@@ -17,6 +17,18 @@ foreign user32 {
|
||||
|
||||
GetClassNameW :: proc(hWnd: HWND, lpClassName: LPWSTR, nMaxCount: c_int) -> c_int ---
|
||||
|
||||
GetParent :: proc(hWnd: HWND) -> HWND ---
|
||||
IsWindowVisible :: proc(hWnd: HWND) -> BOOL ---
|
||||
SetWinEventHook :: proc(
|
||||
eventMin, eventMax: DWORD,
|
||||
hmodWinEventProc: HMODULE,
|
||||
pfnWinEvenProc: WINEVENTPROC,
|
||||
idProcess, idThread: DWORD,
|
||||
dwFlags: WinEventFlags,
|
||||
) -> HWINEVENTHOOK ---
|
||||
|
||||
IsChild :: proc(hWndParent, hWnd: HWND) -> BOOL ---
|
||||
|
||||
RegisterClassW :: proc(lpWndClass: ^WNDCLASSW) -> ATOM ---
|
||||
RegisterClassExW :: proc(^WNDCLASSEXW) -> ATOM ---
|
||||
UnregisterClassW :: proc(lpClassName: LPCWSTR, hInstance: HINSTANCE) -> BOOL ---
|
||||
@@ -568,3 +580,12 @@ RedrawWindowFlags :: enum UINT {
|
||||
RDW_FRAME = 0x0400,
|
||||
RDW_NOFRAME = 0x0800,
|
||||
}
|
||||
|
||||
// OUTOFCONTEXT is the zero value, use {}
|
||||
WinEventFlags :: bit_set[WinEventFlag; DWORD]
|
||||
|
||||
WinEventFlag :: enum DWORD {
|
||||
SKIPOWNTHREAD = 0,
|
||||
SKIPOWNPROCESS = 1,
|
||||
INCONTEXT = 2,
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ ERROR_PIPE_BUSY : DWORD : 231
|
||||
|
||||
E_NOTIMPL :: HRESULT(-0x7fff_bfff) // 0x8000_4001
|
||||
|
||||
SUCCEEDED :: #force_inline proc(#any_int result: int) -> bool { return result >= 0 }
|
||||
SUCCEEDED :: #force_inline proc "contextless" (#any_int result: int) -> bool { return result >= 0 }
|
||||
|
||||
|
||||
System_Error :: enum DWORD {
|
||||
|
||||
@@ -8,13 +8,22 @@ import "core:strings"
|
||||
import "core:sync/chan"
|
||||
import "core:time"
|
||||
|
||||
Default_Test_Logger_Opts :: runtime.Logger_Options {
|
||||
.Level,
|
||||
.Terminal_Color,
|
||||
.Short_File_Path,
|
||||
.Line,
|
||||
.Procedure,
|
||||
.Date, .Time,
|
||||
when USING_SHORT_LOGS {
|
||||
Default_Test_Logger_Opts :: runtime.Logger_Options {
|
||||
.Level,
|
||||
.Terminal_Color,
|
||||
.Short_File_Path,
|
||||
.Line,
|
||||
}
|
||||
} else {
|
||||
Default_Test_Logger_Opts :: runtime.Logger_Options {
|
||||
.Level,
|
||||
.Terminal_Color,
|
||||
.Short_File_Path,
|
||||
.Line,
|
||||
.Procedure,
|
||||
.Date, .Time,
|
||||
}
|
||||
}
|
||||
|
||||
Log_Message :: struct {
|
||||
|
||||
+31
-13
@@ -41,6 +41,8 @@ PROGRESS_WIDTH : int : #config(ODIN_TEST_PROGRESS_WIDTH, 24)
|
||||
SHARED_RANDOM_SEED : u64 : #config(ODIN_TEST_RANDOM_SEED, 0)
|
||||
// Set the lowest log level for this test run.
|
||||
LOG_LEVEL : string : #config(ODIN_TEST_LOG_LEVEL, "info")
|
||||
// Show only the most necessary logging information.
|
||||
USING_SHORT_LOGS : bool : #config(ODIN_TEST_SHORT_LOGS, false)
|
||||
|
||||
|
||||
get_log_level :: #force_inline proc() -> runtime.Logger_Level {
|
||||
@@ -497,6 +499,7 @@ runner :: proc(internal_tests: []Internal_Test) -> bool {
|
||||
data.it = it
|
||||
data.t.seed = shared_random_seed
|
||||
data.t.error_count = 0
|
||||
data.t._fail_now_called = false
|
||||
|
||||
thread.pool_add_task(&pool, task.allocator, run_test_task, data, run_index)
|
||||
}
|
||||
@@ -604,10 +607,10 @@ runner :: proc(internal_tests: []Internal_Test) -> bool {
|
||||
})
|
||||
fmt.assertf(alloc_error == nil, "Error appending to log messages: %v", alloc_error)
|
||||
|
||||
find_task_data: for &data in task_data_slots {
|
||||
find_task_data_for_timeout: for &data in task_data_slots {
|
||||
if data.it.pkg == it.pkg && data.it.name == it.name {
|
||||
end_t(&data.t)
|
||||
break find_task_data
|
||||
break find_task_data_for_timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -645,21 +648,36 @@ runner :: proc(internal_tests: []Internal_Test) -> bool {
|
||||
"A signal (%v) was raised to stop test #%i %s.%s, but it was unable to be found.",
|
||||
reason, test_index, it.pkg, it.name)
|
||||
|
||||
if test_index not_in failed_test_reason_map {
|
||||
// We only write a new error message here if there wasn't one
|
||||
// already, because the message we can provide based only on
|
||||
// the signal won't be very useful, whereas asserts and panics
|
||||
// will provide a user-written error message.
|
||||
failed_test_reason_map[test_index] = fmt.aprintf("Signal caught: %v", reason, allocator = shared_log_allocator)
|
||||
pkg_log.fatalf("Caught signal to stop test #%i %s.%s for: %v.", test_index, it.pkg, it.name, reason)
|
||||
|
||||
// The order this is handled in is a little particular.
|
||||
task_data: ^Task_Data
|
||||
find_task_data_for_stop_signal: for &data in task_data_slots {
|
||||
if data.it.pkg == it.pkg && data.it.name == it.name {
|
||||
task_data = &data
|
||||
break find_task_data_for_stop_signal
|
||||
}
|
||||
}
|
||||
|
||||
when FANCY_OUTPUT {
|
||||
bypass_progress_overwrite = true
|
||||
signals_were_raised = true
|
||||
fmt.assertf(task_data != nil, "A signal (%v) was raised to stop test #%i %s.%s, but its task data is missing.",
|
||||
reason, test_index, it.pkg, it.name)
|
||||
|
||||
if !task_data.t._fail_now_called {
|
||||
if test_index not_in failed_test_reason_map {
|
||||
// We only write a new error message here if there wasn't one
|
||||
// already, because the message we can provide based only on
|
||||
// the signal won't be very useful, whereas asserts and panics
|
||||
// will provide a user-written error message.
|
||||
failed_test_reason_map[test_index] = fmt.aprintf("Signal caught: %v", reason, allocator = shared_log_allocator)
|
||||
pkg_log.fatalf("Caught signal to stop test #%i %s.%s for: %v.", test_index, it.pkg, it.name, reason)
|
||||
}
|
||||
|
||||
when FANCY_OUTPUT {
|
||||
bypass_progress_overwrite = true
|
||||
signals_were_raised = true
|
||||
}
|
||||
}
|
||||
|
||||
end_t(&task_data.t)
|
||||
|
||||
total_failure_count += 1
|
||||
total_done_count += 1
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ T :: struct {
|
||||
// tests during channel transmission.
|
||||
_log_allocator: runtime.Allocator,
|
||||
|
||||
_fail_now: proc() -> !,
|
||||
_fail_now_called: bool,
|
||||
}
|
||||
|
||||
|
||||
@@ -66,15 +66,20 @@ fail :: proc(t: ^T, loc := #caller_location) {
|
||||
pkg_log.error("FAIL", location=loc)
|
||||
}
|
||||
|
||||
fail_now :: proc(t: ^T, msg := "", loc := #caller_location) {
|
||||
// fail_now will cause a test to immediately fail and abort, much in the same
|
||||
// way a failed assertion or panic call will stop a thread.
|
||||
//
|
||||
// It is for when you absolutely need a test to fail without calling any of its
|
||||
// deferred statements. It will be cleaner than a regular assert or panic,
|
||||
// as the test runner will know to expect the signal this procedure will raise.
|
||||
fail_now :: proc(t: ^T, msg := "", loc := #caller_location) -> ! {
|
||||
t._fail_now_called = true
|
||||
if msg != "" {
|
||||
pkg_log.error("FAIL:", msg, location=loc)
|
||||
} else {
|
||||
pkg_log.error("FAIL", location=loc)
|
||||
}
|
||||
if t._fail_now != nil {
|
||||
t._fail_now()
|
||||
}
|
||||
runtime.trap()
|
||||
}
|
||||
|
||||
failed :: proc(t: ^T) -> bool {
|
||||
@@ -94,7 +99,17 @@ logf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) {
|
||||
|
||||
// cleanup registers a procedure and user_data, which will be called when the test, and all its subtests, complete.
|
||||
// Cleanup procedures will be called in LIFO (last added, first called) order.
|
||||
// Each procedure will use a copy of the context at the time of registering.
|
||||
//
|
||||
// Each procedure will use a copy of the context at the time of registering,
|
||||
// and if the test failed due to a timeout, failed assertion, panic, bounds-checking error,
|
||||
// memory access violation, or any other signal-based fault, this procedure will
|
||||
// run with greater privilege in the test runner's main thread.
|
||||
//
|
||||
// That means that any cleanup procedure absolutely must not fail in the same way,
|
||||
// or it will take down the entire test runner with it. This is for when you
|
||||
// need something to run no matter what, if a test failed.
|
||||
//
|
||||
// For almost every usual case, `defer` should be preferable and sufficient.
|
||||
cleanup :: proc(t: ^T, procedure: proc(rawptr), user_data: rawptr) {
|
||||
append(&t.cleanups, Internal_Cleanup{procedure, user_data, context})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user