Compare commits

...

5 commits

Author SHA1 Message Date
Delyan Angelov
08a739d793
ci: cleanup .gitattributes, runes.txt and input_rune_iterator_test.v
Some checks failed
Graphics CI / gg-regressions (push) Waiting to run
vlib modules CI / build-module-docs (push) Waiting to run
Shy and PV CI / v-compiles-puzzle-vibes (push) Waiting to run
sdl CI / v-compiles-sdl-examples (push) Waiting to run
Time CI / time-linux (push) Waiting to run
Time CI / time-macos (push) Waiting to run
Time CI / time-windows (push) Waiting to run
toml CI / toml-module-pass-external-test-suites (push) Waiting to run
Tools CI / tools-linux (clang) (push) Waiting to run
Tools CI / tools-linux (gcc) (push) Waiting to run
Tools CI / tools-linux (tcc) (push) Waiting to run
Tools CI / tools-macos (clang) (push) Waiting to run
Tools CI / tools-windows (gcc) (push) Waiting to run
Tools CI / tools-windows (msvc) (push) Waiting to run
Tools CI / tools-windows (tcc) (push) Waiting to run
Tools CI / tools-docker-ubuntu-musl (push) Waiting to run
vab CI / vab-compiles-v-examples (push) Waiting to run
vab CI / v-compiles-os-android (push) Waiting to run
native backend CI / native-backend-ubuntu (push) Has been cancelled
native backend CI / native-backend-windows (push) Has been cancelled
Sanitized CI / sanitize-undefined-clang (push) Has been cancelled
Sanitized CI / sanitize-undefined-gcc (push) Has been cancelled
Sanitized CI / tests-sanitize-address-clang (push) Has been cancelled
Sanitized CI / sanitize-address-msvc (push) Has been cancelled
Sanitized CI / sanitize-address-gcc (push) Has been cancelled
Sanitized CI / sanitize-memory-clang (push) Has been cancelled
wasm backend CI / wasm-backend (ubuntu-22.04) (push) Has been cancelled
wasm backend CI / wasm-backend (windows-2022) (push) Has been cancelled
2025-09-05 18:34:13 +03:00
Mike
1820a5584b
math.big: replace division with Knuth, improve performance (#25242) 2025-09-05 16:47:25 +03:00
Delyan Angelov
4dcf79e388 os,ci: use a conditional include, instead of a platform file, to include libproc.h on macos only 2025-09-05 12:50:30 +03:00
Delyan Angelov
35510bc659
builtin: add input_rune/0 and input_rune_iterator/0 + tests (#25238) 2025-09-05 12:39:02 +03:00
kbkpbot
cf61f4fa1b
builtin: fix C prefix for proc_pidpath() (#25239) 2025-09-05 12:23:24 +03:00
9 changed files with 233 additions and 240 deletions

View file

@ -54,7 +54,7 @@ fn C.isdigit(c int) bool
fn C.popen(c &char, t &char) voidptr
// <libproc.h>
pub fn proc_pidpath(int, voidptr, int) int
fn C.proc_pidpath(int, voidptr, int) int
fn C.realpath(const_path &char, resolved_path &char) &char

View file

@ -0,0 +1,40 @@
module builtin
// input_rune returns a single rune from the standart input (an unicode codepoint).
// It expects, that the input is utf8 encoded.
// It will return `none` on EOF.
pub fn input_rune() ?rune {
x := input_character()
if x <= 0 {
return none
}
char_len := utf8_char_len(u8(x))
if char_len == 1 {
return x
}
mut b := u8(x)
b = b << char_len
mut res := rune(b)
mut shift := 6 - char_len
for i := 1; i < char_len; i++ {
c := rune(input_character())
res = rune(res) << shift
res |= c & 63 // 0x3f
shift = 6
}
return res
}
// InputRuneIterator is an iterator over the input runes.
pub struct InputRuneIterator {}
// next returns the next rune from the input stream.
pub fn (mut self InputRuneIterator) next() ?rune {
return input_rune()
}
// input_rune_iterator returns an iterator to allow for `for i, r in input_rune_iterator() {`.
// When the input stream is closed, the loop will break.
pub fn input_rune_iterator() InputRuneIterator {
return InputRuneIterator{}
}

View file

@ -0,0 +1,52 @@
// vtest build: !windows
// vtest retry: 2
import os
import time
fn test_input_rune_iterator_with_unicode_input() {
mut p := os.new_process(@VEXE)
p.set_args(['-e', 'for i, r in input_rune_iterator() { println("> i: \${i:04} | r: `\${r}`") }'])
p.set_redirect_stdio()
p.run()
spawn fn [mut p] () {
time.sleep(10 * time.millisecond)
dump(p.pid)
p.stdin_write('Проба Abc 🌍 123')
time.sleep(10 * time.millisecond)
p.stdin_write('\0x00') // 0 should break the input stream
time.sleep(10 * time.millisecond)
eprintln('>>> done')
}()
mut olines := []string{}
for p.is_alive() {
if oline := p.pipe_read(.stdout) {
olines << oline
}
time.sleep(1 * time.millisecond)
}
p.close()
p.wait()
assert p.code == 0
eprintln('done')
solines := olines.join('\n').trim_space().replace('\r', '')
eprintln('solines.len: ${solines.len} | solines: ${solines}')
assert solines.len > 100
assert solines == '> i: 0000 | r: `П`
> i: 0001 | r: `р`
> i: 0002 | r: `о`
> i: 0003 | r: `б`
> i: 0004 | r: `а`
> i: 0005 | r: ` `
> i: 0006 | r: `A`
> i: 0007 | r: `b`
> i: 0008 | r: `c`
> i: 0009 | r: ` `
> i: 0010 | r: ``
> i: 0011 | r: ``
> i: 0012 | r: ` `
> i: 0013 | r: `🌍`
> i: 0014 | r: ` `
> i: 0015 | r: `1`
> i: 0016 | r: `2`
> i: 0017 | r: `3`'
}

View file

@ -181,10 +181,7 @@ fn divide_digit_array(operand_a []u64, operand_b []u64, mut quotient []u64, mut
cmp_result := compare_digit_array(operand_a, operand_b)
// a == b => q, r = 1, 0
if cmp_result == 0 {
quotient << 1
for quotient.len > 1 {
quotient.delete_last()
}
quotient[0] = 1
remainder.clear()
return
}
@ -192,7 +189,9 @@ fn divide_digit_array(operand_a []u64, operand_b []u64, mut quotient []u64, mut
// a < b => q, r = 0, a
if cmp_result < 0 {
quotient.clear()
remainder << operand_a
for i in 0 .. operand_a.len {
remainder[i] = operand_a[i]
}
return
}
if operand_b.len == 1 {
@ -210,38 +209,36 @@ fn divide_array_by_digit(operand_a []u64, divisor u64, mut quotient []u64, mut r
// 1 digit for both dividend and divisor
dividend := operand_a[0]
q := dividend / divisor
if q != 0 {
quotient << q
}
quotient[0] = q
rem := dividend % divisor
if rem != 0 {
remainder << rem
}
remainder[0] = rem
shrink_tail_zeros(mut quotient)
shrink_tail_zeros(mut remainder)
return
}
// Dividend has more digits
mut rem := u64(0)
mut quo := u64(0)
mut qtemp := []u64{len: quotient.cap}
divisor64 := u64(divisor)
// Perform division step by step
for index := operand_a.len - 1; index >= 0; index-- {
hi := rem >> (64 - digit_bits)
lo := rem << digit_bits | operand_a[index]
quo, rem = bits.div_64(hi, lo, divisor64)
qtemp[index] = quo & max_digit
quo, rem = bits.div_64(hi, lo, divisor)
quotient[index] = quo & max_digit
}
// Remove leading zeros from quotient
shrink_tail_zeros(mut qtemp)
quotient << qtemp
remainder << rem
shrink_tail_zeros(mut quotient)
remainder[0] = rem
shrink_tail_zeros(mut remainder)
}
@[inline]
fn divide_array_by_array(operand_a []u64, operand_b []u64, mut quotient []u64, mut remainder []u64) {
binary_divide_array_by_array(operand_a, operand_b, mut quotient, mut remainder)
knuth_divide_array_by_array(operand_a, operand_b, mut quotient, mut remainder)
}
// Shifts the contents of the original array by the given amount of bits to the left.

View file

@ -136,8 +136,8 @@ fn test_compare_digit_array_02() {
fn test_divide_digit_array_01() {
a := [u64(14)]
b := [u64(2)]
mut q := []u64{cap: 1}
mut r := []u64{cap: 1}
mut q := []u64{len: 1}
mut r := []u64{len: 1}
divide_digit_array(a, b, mut q, mut r)
assert q == [u64(7)]
@ -147,8 +147,8 @@ fn test_divide_digit_array_01() {
fn test_divide_digit_array_02() {
a := [u64(14)]
b := [u64(15)]
mut q := []u64{cap: 1}
mut r := []u64{cap: 1}
mut q := []u64{len: 1}
mut r := []u64{len: 1}
divide_digit_array(a, b, mut q, mut r)
assert q == []u64{len: 0}
@ -158,8 +158,8 @@ fn test_divide_digit_array_02() {
fn test_divide_digit_array_03() {
a := [u64(0), 4]
b := [u64(0), 1]
mut q := []u64{cap: a.len - b.len + 1}
mut r := []u64{cap: a.len}
mut q := []u64{len: a.len - b.len + 1}
mut r := []u64{len: a.len}
divide_digit_array(a, b, mut q, mut r)
assert q == [u64(4)]
@ -169,8 +169,8 @@ fn test_divide_digit_array_03() {
fn test_divide_digit_array_04() {
a := [u64(2), 4]
b := [u64(0), 1]
mut q := []u64{cap: a.len - b.len + 1}
mut r := []u64{cap: a.len}
mut q := []u64{len: a.len - b.len + 1}
mut r := []u64{len: a.len}
divide_digit_array(a, b, mut q, mut r)
assert q == [u64(4)]
@ -180,8 +180,8 @@ fn test_divide_digit_array_04() {
fn test_divide_digit_array_05() {
a := [u64(3)]
b := [u64(2)]
mut q := []u64{cap: a.len - b.len + 1}
mut r := []u64{cap: a.len}
mut q := []u64{len: a.len - b.len + 1}
mut r := []u64{len: a.len}
divide_digit_array(a, b, mut q, mut r)
assert q == [u64(1)]

View file

@ -2,57 +2,6 @@ module big
import math.bits
// suppose operand_a bigger than operand_b and both not null.
// Both quotient and remaider are allocated but of length 0
@[direct_array_access]
fn binary_divide_array_by_array(operand_a []u64, operand_b []u64, mut quotient []u64, mut remainder []u64) {
remainder << operand_a
len_diff := operand_a.len - operand_b.len
$if debug {
assert len_diff >= 0
}
// we must do in place shift and operations.
mut divisor := []u64{cap: operand_b.len}
for _ in 0 .. len_diff {
divisor << u64(0)
}
divisor << operand_b
for _ in 0 .. len_diff + 1 {
quotient << u64(0)
}
lead_zer_remainder := u32(bits.leading_zeros_64(remainder.last()) - (64 - digit_bits))
lead_zer_divisor := u32(bits.leading_zeros_64(divisor.last()) - (64 - digit_bits))
bit_offset := (u32(digit_bits) * u32(len_diff)) + (lead_zer_divisor - lead_zer_remainder)
// align
if lead_zer_remainder < lead_zer_divisor {
left_shift_in_place(mut divisor, lead_zer_divisor - lead_zer_remainder)
} else if lead_zer_remainder > lead_zer_divisor {
left_shift_in_place(mut remainder, lead_zer_remainder - lead_zer_divisor)
}
$if debug {
assert left_align_p(divisor[divisor.len - 1], remainder[remainder.len - 1])
}
for bit_idx := int(bit_offset); bit_idx >= 0; bit_idx-- {
if greater_equal_from_end(remainder, divisor) {
bit_set(mut quotient, bit_idx)
subtract_align_last_byte_in_place(mut remainder, divisor)
}
right_shift_in_place(mut divisor, 1)
}
// adjust
if lead_zer_remainder > lead_zer_divisor {
right_shift_in_place(mut remainder, lead_zer_remainder - lead_zer_divisor)
}
shrink_tail_zeros(mut remainder)
shrink_tail_zeros(mut quotient)
}
// help routines for cleaner code but inline for performance
// quicker than BitField.set_bit
@[direct_array_access; inline]
@ -65,80 +14,112 @@ fn bit_set(mut a []u64, n int) {
a[byte_offset] |= mask
}
// a.len is greater or equal to b.len
// returns true if a >= b (completed with zeroes)
@[direct_array_access; inline]
fn greater_equal_from_end(a []u64, b []u64) bool {
$if debug {
assert a.len >= b.len
}
offset := a.len - b.len
for index := a.len - 1; index >= offset; index-- {
if a[index] > b[index - offset] {
return true
} else if a[index] < b[index - offset] {
return false
}
}
return true
}
@[direct_array_access]
fn knuth_divide_array_by_array(operand_a []u64, operand_b []u64, mut quotient []u64, mut remainder []u64) {
m := operand_a.len - operand_b.len
n := operand_b.len
mut u := []u64{len: operand_a.len + 1}
mut v := []u64{len: n}
leading_zeros := bits.leading_zeros_64(operand_b.last()) - (64 - digit_bits)
// a := a - b supposed a >= b
// attention the b operand is align with the a operand before the subtraction
@[direct_array_access; inline]
fn subtract_align_last_byte_in_place(mut a []u64, b []u64) {
if leading_zeros > 0 {
mut carry := u64(0)
mut new_carry := u64(0)
offset := a.len - b.len
for index := a.len - b.len; index < a.len; index++ {
if a[index] < (b[index - offset] + carry) || (b[index - offset] == max_digit && carry > 0) {
new_carry = 1
amount := digit_bits - leading_zeros
for i in 0 .. operand_a.len {
temp := (operand_a[i] << leading_zeros) | carry
u[i] = temp & max_digit
carry = operand_a[i] >> amount
}
u[operand_a.len] = carry
carry = 0
for i in 0 .. operand_b.len {
temp := (operand_b[i] << leading_zeros) | carry
v[i] = temp & max_digit
carry = operand_b[i] >> amount
}
} else {
new_carry = 0
for i in 0 .. operand_a.len {
u[i] = operand_a[i]
}
a[index] -= (b[index - offset] + carry)
a[index] = a[index] & max_digit
carry = new_carry
for i in 0 .. operand_b.len {
v[i] = operand_b[i]
}
$if debug {
assert carry == 0
}
}
// logical left shift
// there is no overflow. We know that the last bits are zero
// and that n <= `digit_bits`
@[direct_array_access; inline]
fn left_shift_in_place(mut a []u64, n u32) {
if remainder.len >= (n + 1) {
remainder.trim(n + 1)
} else {
remainder = []u64{len: n + 1}
}
v_n_1 := v[n - 1]
v_n_2 := v[n - 2]
for j := m; j >= 0; j-- {
u_j_n := u[j + n]
u_j_n_1 := u[j + n - 1]
u_j_n_2 := u[j + n - 2]
mut qhat, mut rhat := bits.div_64(u_j_n >> (64 - digit_bits), (u_j_n << digit_bits) | u_j_n_1,
v_n_1)
mut x1, mut x2 := bits.mul_64(qhat, v_n_2)
x2 = x2 & max_digit
x1 = (x1 << (64 - digit_bits)) | (x2 >> digit_bits)
for greater_than(x1, x2, rhat, u_j_n_2) {
qhat--
prev := rhat
rhat += v_n_1
if rhat < prev {
break
}
x1, x2 = bits.mul_64(qhat, v_n_2)
x2 = x2 & max_digit
x1 = (x1 << (64 - digit_bits)) | (x2 >> digit_bits)
}
mut carry := u64(0)
mut prec_carry := u64(0)
mask := ((u64(1) << n) - 1) << (digit_bits - n)
for index in 0 .. a.len {
prec_carry = carry >> (digit_bits - n)
carry = a[index] & mask
a[index] <<= n
a[index] = a[index] & max_digit
a[index] |= prec_carry
for i in 0 .. n {
hi, lo := bits.mul_add_64(v[i], qhat, carry)
remainder[i] = lo & max_digit
carry = (hi << (64 - digit_bits)) | (lo >> digit_bits)
}
}
remainder[n] = carry
// logical right shift without control because these digits have already been
// shift left before
@[direct_array_access; inline]
fn right_shift_in_place(mut a []u64, n u32) {
mut borrow := u64(0)
for i in 0 .. n + 1 {
result := u[j + i] - remainder[i] - borrow
u[j + i] = result & max_digit
borrow = (result >> digit_bits) & 1
}
if borrow == 1 {
qhat--
carry = u64(0)
for i in 0 .. n {
sum := u[j + i] + v[i] + carry
u[j + i] = sum & max_digit
carry = sum >> digit_bits
}
}
quotient[j] = qhat
}
remainder.delete_last()
if leading_zeros > 0 {
mut carry := u64(0)
mut prec_carry := u64(0)
mask := (u64(1) << n) - 1
for index := a.len - 1; index >= 0; index-- {
carry = a[index] & mask
a[index] >>= n
a[index] |= prec_carry << (digit_bits - n)
prec_carry = carry
max_leading_digit := (u64(1) << leading_zeros) - 1
for i := n - 1; i >= 0; i-- {
current_limb := u[i]
remainder[i] = (current_limb >> leading_zeros) | carry
carry = (current_limb & max_leading_digit) << (digit_bits - leading_zeros)
}
} else {
for i in 0 .. n {
remainder[i] = u[i]
}
}
shrink_tail_zeros(mut quotient)
shrink_tail_zeros(mut remainder)
}
// for assert
@[inline]
fn left_align_p(a u64, b u64) bool {
return bits.leading_zeros_64(a) == bits.leading_zeros_64(b)
fn greater_than(x1 u64, x2 u64, y1 u64, y2 u64) bool {
return x1 > y1 || (x1 == y1 && x2 > y2)
}

View file

@ -2,92 +2,11 @@ module big
import rand
fn test_left_shift_in_place() {
mut a := [u64(1), 1, 1, 1, 1]
left_shift_in_place(mut a, 1)
assert a == [u64(2), 2, 2, 2, 2]
left_shift_in_place(mut a, 7)
assert a == [u64(256), 256, 256, 256, 256]
mut b := [u64(0x08000000_00000001), 0x0c000000_00000000, 0x08000000_00000000, 0x07ffffff_ffffffff]
left_shift_in_place(mut b, 1)
assert b == [u64(2), 0x08000000_00000001, 1, 0x0fffffff_ffffffff]
mut c := [u64(0x00ffffff_ffffffff), 0x00f0f0f0_f0f0f0f0, 1, 0x03ffffff_ffffffff, 1]
left_shift_in_place(mut c, 2)
assert c == [u64(0x03ffffff_fffffffc), 0x03c3c3c3_c3c3c3c0, 4, 0x0fffffff_fffffffc, 4]
}
fn test_right_shift_in_place() {
mut a := [u64(2), 2, 2, 2, 2]
right_shift_in_place(mut a, 1)
assert a == [u64(1), 1, 1, 1, 1]
a = [u64(256), 256, 256, 256, 256]
right_shift_in_place(mut a, 7)
assert a == [u64(2), 2, 2, 2, 2]
a = [u64(0), 0, 1]
right_shift_in_place(mut a, 1)
assert a == [u64(0), 0x08000000_00000000, 0]
mut b := [u64(3), 0x08000000_00000001, 1, 0x0fffffff_ffffffff]
right_shift_in_place(mut b, 1)
assert b == [u64(0x08000000_00000001), 0x0c000000_00000000, 0x08000000_00000000,
0x07ffffff_ffffffff]
mut c := [u64(0x03ffffff), 0x03c3c3c3_c3c3c3c0, 7, 0xfffffffc, 4]
right_shift_in_place(mut c, 2)
assert c == [u64(0x00ffffff), 0x0cf0f0f0_f0f0f0f0, 1, 0x3fffffff, 1]
}
fn test_subtract_align_last_byte_in_place() {
mut a := [u64(2), 2, 2, 2, 2]
mut b := [u64(1), 1, 2, 1, 1]
subtract_align_last_byte_in_place(mut a, b)
assert a == [u64(1), 1, 0, 1, 1]
a = [u64(0), 0, 0, 0, 1]
b = [u64(0), 0, 1]
subtract_align_last_byte_in_place(mut a, b)
assert a == [u64(0), 0, 0, 0, 0]
a = [u64(0), 0, 0, 0, 1, 13]
b = [u64(1), 0, 1]
mut c := []u64{len: a.len}
mut d := [u64(0), 0, 0]
d << b // to have same length
subtract_digit_array(a, d, mut c)
subtract_align_last_byte_in_place(mut a, b)
assert a == [u64(0), 0, 0, u64(-1) & max_digit, 0, 12]
assert c == a
}
fn test_greater_equal_from_end() {
mut a := [u64(1), 2, 3, 4, 5, 6]
mut b := [u64(3), 4, 5, 6]
assert greater_equal_from_end(a, b) == true
a = [u64(1), 2, 3, 4, 5, 6]
b = [u64(1), 2, 3, 4, 5, 6]
assert greater_equal_from_end(a, b) == true
a = [u64(1), 2, 3, 4, 5, 6]
b = [u64(2), 2, 3, 4, 5, 6]
assert greater_equal_from_end(a, b) == false
a = [u64(0), 0, 0, 4, 5, 6]
b = [u64(4), 5, 6]
assert greater_equal_from_end(a, b) == true
a = [u64(0), 0, 0, 4, 5, 6]
b = [u64(4), 6, 6]
assert greater_equal_from_end(a, b) == false
a = [u64(0), 0, 0, 4, 5, 5]
b = [u64(4), 5, 6]
assert greater_equal_from_end(a, b) == false
}
fn test_divide_digit_array_03() {
a := [u64(0), 4]
b := [u64(0), 1]
mut q := []u64{cap: a.len - b.len + 1}
mut r := []u64{cap: a.len}
mut q := []u64{len: a.len - b.len + 1}
mut r := []u64{len: a.len}
divide_digit_array(a, b, mut q, mut r)
assert q == [u64(4)]
@ -97,8 +16,8 @@ fn test_divide_digit_array_03() {
fn test_divide_digit_array_04() {
a := [u64(2), 4]
b := [u64(0), 1]
mut q := []u64{cap: a.len - b.len + 1}
mut r := []u64{cap: a.len}
mut q := []u64{len: a.len - b.len + 1}
mut r := []u64{len: a.len}
divide_digit_array(a, b, mut q, mut r)
assert q == [u64(4)]
@ -108,8 +27,8 @@ fn test_divide_digit_array_04() {
fn test_divide_digit_array_05() {
a := [u64(2), 4, 5]
b := [u64(0), 1]
mut q := []u64{cap: a.len - b.len + 1}
mut r := []u64{cap: a.len}
mut q := []u64{len: a.len - b.len + 1}
mut r := []u64{len: a.len}
divide_digit_array(a, b, mut q, mut r)
assert q == [u64(4), 5]
@ -119,8 +38,8 @@ fn test_divide_digit_array_05() {
fn test_divide_digit_array_06() {
a := [u64(2), 4, 5, 3]
b := [u64(0), 0x8000]
mut q := []u64{cap: a.len - b.len + 1}
mut r := []u64{cap: a.len}
mut q := []u64{len: a.len - b.len + 1}
mut r := []u64{len: a.len}
divide_digit_array(a, b, mut q, mut r)
assert q == [u64(0xa000_00000000), 0x6000_00000000]

View file

@ -393,8 +393,8 @@ pub fn (multiplicand Integer) * (multiplier Integer) Integer {
//
// DO NOT use this method if the divisor has any chance of being 0.
fn (dividend Integer) div_mod_internal(divisor Integer) (Integer, Integer) {
mut q := []u64{cap: int_max(1, dividend.digits.len - divisor.digits.len + 1)}
mut r := []u64{cap: dividend.digits.len}
mut q := []u64{len: int_max(1, dividend.digits.len - divisor.digits.len + 1)}
mut r := []u64{len: dividend.digits.len}
mut q_signum := 0
mut r_signum := 0

View file

@ -5,6 +5,10 @@ import strings
#include <sys/stat.h> // #include <signal.h>
#include <errno.h>
$if macos {
#include <libproc.h>
}
$if freebsd || openbsd {
#include <sys/sysctl.h>
}
@ -715,7 +719,7 @@ pub fn executable() string {
}
$if macos {
pid := C.getpid()
ret := proc_pidpath(pid, &result[0], max_path_len)
ret := C.proc_pidpath(pid, &result[0], max_path_len)
if ret <= 0 {
eprintln('os.executable() failed at calling proc_pidpath with pid: ${pid} . proc_pidpath returned ${ret} ')
return executable_fallback()