From 8c2eb52d4c94aa08a11eedad23b99c55597a00fc Mon Sep 17 00:00:00 2001 From: Francois-Rene Rideau Date: Sat, 18 Nov 2023 01:06:52 +0000 Subject: [PATCH 1/8] std/misc/vector --- src/std/build-spec.ss | 1 + src/std/misc/vector-test.ss | 41 ++++++++++++++++++ src/std/misc/vector.ss | 86 +++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 src/std/misc/vector-test.ss create mode 100644 src/std/misc/vector.ss diff --git a/src/std/build-spec.ss b/src/std/build-spec.ss index 03d1501e6..a4c01be6d 100644 --- a/src/std/build-spec.ss +++ b/src/std/build-spec.ss @@ -323,6 +323,7 @@ "misc/atom" "misc/dag" "misc/decimal" + "misc/vector" "misc/evector" "misc/prime" "misc/concurrent-plan" diff --git a/src/std/misc/vector-test.ss b/src/std/misc/vector-test.ss new file mode 100644 index 000000000..d22d3507c --- /dev/null +++ b/src/std/misc/vector-test.ss @@ -0,0 +1,41 @@ +(export vector-test) + +(import + (only-in :std/misc/list-builder with-list-builder) + :std/test + ./vector) + +(def vector-test + (test-suite "test suite for std/misc/vector" + (test-case "vector" + (def foo (vector 1 2 3)) + (set! (vector-ref foo 1) 4) + (check foo => #(1 4 3)) + (check (vector-least-index (cut < 10) #(35 21 16 11 10 9 7 4 1)) => 5) + (check (vector-most-index (cut < 10) #(2 3 5 7 11 13 17 19 23)) => 4) + (check (maybe-subvector #(1 3 5 7) 2) => #(5 7)) + (check (eq? foo (maybe-subvector foo 0 3)) => #t) + (check (with-list-builder (c) + (subvector-for-each + c #(a b c d e f g h) start: 2 end: 5)) + => '(c d e)) + (check (with-list-builder (c) + (subvector-for-each/index + (lambda (x y) (c [x y])) #(a b c d e f g h) start: 5)) + => '((f 5) (g 6) (h 7))) + (check (with-list-builder (c) + (subvector-reverse-for-each c #(a b c d e f g h) start: 2 end: 5)) + => '(e d c)) + (check (with-list-builder (c) + (subvector-reverse-for-each/index + (lambda (x y) (c [x y])) #(a b c d e f g h) start: 5)) + => '((h 7) (g 6) (f 5))) + (check (subvector->list #(a b c d e f g h) start: 5) + => '(c d e)) + (check (cons->vector '(a . b)) + => #(a b)) + (check (cons->vector 'foo) + => #f) + (check (vector-filter odd? #(1 2 3 4 5 6 7 8 9) start: 1 end: 7) + => #(3 5 7)) + )) diff --git a/src/std/misc/vector.ss b/src/std/misc/vector.ss new file mode 100644 index 000000000..4d84ddb9e --- /dev/null +++ b/src/std/misc/vector.ss @@ -0,0 +1,86 @@ +;; -*- Gerbil -*- +;;;; Utilities pertaining to using Vectors + +(export #t + (rename: srfi43-vector-map vector-map/index) + (rename: srfi43-vector-for-each vector-for-each/index) + (rename: srfi43-vector-map! vector-map!/index) + (rename: srfi43-vector-fold vector-fold/index) + (rename: srfi43-vector-fold-right vector-fold-right/index) + (rename: srfi43-vector-count vector-count/index)) + +(import + (prefix-in (only-in :std/srfi/43 + vector-map + vector-for-each + vector-map! + vector-fold + vector-fold-right + vector-count) srfi43-) + (only-in :std/iter for in-range in-iota) + (only-in :std/misc/list-builder with-list-builder) + (only-in :std/misc/number least-integer) + (only-in :std/sugar while)) + +;; Enable (set! (vector-ref v i) x) +(defalias vector-ref-set! vector-set!) + +;;; Assuming a sorted vector, a predicate on vector elements that is "increasing", +;;; i.e. if true, true on all subsequent elements, and optionally +;;; a start (inclusive, defaults to 0) and an end (exclusive, defaults to vector length), +;;; return the least index of a vector element in the interval [start, env) +;;; that satisfies the predicate, or the end if none does. +(def (vector-least-index pred? vector start: (start 0) end: (end (vector-length vector))) + (least-integer (lambda (i) (pred? (vector-ref vector i))) start end)) + +;;; Assuming a sorted vector, a predicate on vector elements that is "decreasing", +;;; i.e. if true, true on all preceding elements, and optionally +;;; a start (inclusive, defaults to 0) and an end (exclusive, defaults to vector length), +;;; return the most index such that all previous vector elements in the interval [start, env) +;;; satisfy the predicate, or the start if none does. +(def (vector-most-index pred? vector start: (start 0) end: (end (vector-length vector))) + (most-integer (lambda (i) (pred? (vector-ref vector i))) start end)) + +;;; Copy a vector if necessary: return the same if no change in start and end requested. +(def (maybe-subvector vector (start 0) (end #f)) + (let* ((len (vector-length vector)) + (end (or end len))) + (if (and (eqv? start 0) (eqv? end len)) + vector + (subvector vector start end)))) + +(def (subvector-for-each function vector start: (start 0) end: (end #f)) + (for ((i (in-iota (- (or end (vector-length vector)) start) start))) + (function (vector-ref vector i)))) + +(def (subvector-for-each/index function vector start: (start 0) end: (end #f)) + (for ((i (in-iota (- (or end (vector-length vector)) start) start))) + (function i (vector-ref vector i)))) + +(def (subvector-reverse-for-each function vector start: (start 0) end: (end #f)) + (let ((end (or end (vector-length vector)))) + (for ((i (in-iota (- end start) (- end 1) -1))) + (function (vector-ref vector i))))) + +(def (subvector-reverse-for-each/index function vector start: (start 0) end: (end #f)) + (def end (or end (vector-length vector))) + (for ((i (in-iota (- end start) (- end 1) -1))) + (function i (vector-ref vector i)))) + +(def (subvector->list vector start: (start 0) end: (end #f)) + (with-list-builder (c!) (subvector-for-each c! vector start: start end: end))) + +(def cons->vector (match <> ([car . cdr] (vector car cdr)) (_ #f))) + +;;;; Given a vector, an index and a function, update the element of the vector at given index +;;;; by invoking the function on its previous value +;;(def (vector-update! vector index fun) +;; (vector-set! vector index (fun (vector-ref vector index)))) + +;; Filter entries of a vector to those that satisfy the predicate +(def (vector-filter pred? v start: (start 0) end: (end (vector-length v))) + (list->vector + (with-list-builder (c) + (for (i (in-range start end)) + (def e (vector-ref v i)) + (when (pred? e) (c e)))))) From be6c77e6d63a69cecc1e8966712588f1d2dadc87 Mon Sep 17 00:00:00 2001 From: Francois-Rene Rideau Date: Sat, 18 Nov 2023 01:38:03 +0000 Subject: [PATCH 2/8] vector fix and docs --- doc/.vuepress/config.js | 1 + doc/reference/std/misc/vector.md | 48 ++++++++++++++++++++++++++++++++ src/std/misc/vector.ss | 2 +- 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 doc/reference/std/misc/vector.md diff --git a/doc/.vuepress/config.js b/doc/.vuepress/config.js index c07f55274..de41e5a2d 100644 --- a/doc/.vuepress/config.js +++ b/doc/.vuepress/config.js @@ -187,6 +187,7 @@ module.exports = { 'misc/number', 'misc/shuffle', 'misc/uuid', + 'misc/vector', ] } ] diff --git a/doc/reference/std/misc/vector.md b/doc/reference/std/misc/vector.md new file mode 100644 index 000000000..5f51d70ca --- /dev/null +++ b/doc/reference/std/misc/vector.md @@ -0,0 +1,48 @@ +# Vector + +The `:std/misc/vector` library provides common vector functions +that complement those provided by RnRS, Gambit and `:std/srfi/43`. + +::: tip To use the bindings from this module: +```scheme +(import :std/misc/vector) +``` +::: + +## vector-ref-set! + +``` scheme +(def vector-ref-set! vector-set!) +(set! (vector-ref v i) x) +``` + +This binding enables you to use `set!` with `vector-ref`. + +::: tip Examples: +``` scheme +> (def foo (vector 1 2 3)) +> (set! (vector-ref foo 1) 4) +> foo +#(1 4 3) +``` +::: + +### vector-least-index + +### vector-most-index + +### maybe-subvector + +### subvector-for-each + +### subvector-for-each/index + +### subvector-reverse-for-each + +### subvector-reverse-for-each/index + +### subvector->list + +### cons->vector + +### vector-filter diff --git a/src/std/misc/vector.ss b/src/std/misc/vector.ss index 4d84ddb9e..52e704e33 100644 --- a/src/std/misc/vector.ss +++ b/src/std/misc/vector.ss @@ -19,7 +19,7 @@ vector-count) srfi43-) (only-in :std/iter for in-range in-iota) (only-in :std/misc/list-builder with-list-builder) - (only-in :std/misc/number least-integer) + (only-in :std/misc/number least-integer most-integer) (only-in :std/sugar while)) ;; Enable (set! (vector-ref v i) x) From c26325470d981dfcec7a21450276759ec9362954 Mon Sep 17 00:00:00 2001 From: Francois-Rene Rideau Date: Sat, 18 Nov 2023 01:45:27 +0000 Subject: [PATCH 3/8] Fix tests --- doc/reference/std/misc/vector.md | 33 +++++++++++++++++++++++++++++++- src/std/misc/number-test.ss | 4 ++-- src/std/misc/vector-test.ss | 16 ++++++++++------ src/std/misc/vector.ss | 10 +++++----- 4 files changed, 49 insertions(+), 14 deletions(-) diff --git a/doc/reference/std/misc/vector.md b/doc/reference/std/misc/vector.md index 5f51d70ca..db7e9e516 100644 --- a/doc/reference/std/misc/vector.md +++ b/doc/reference/std/misc/vector.md @@ -10,7 +10,6 @@ that complement those provided by RnRS, Gambit and `:std/srfi/43`. ::: ## vector-ref-set! - ``` scheme (def vector-ref-set! vector-set!) (set! (vector-ref v i) x) @@ -28,8 +27,40 @@ This binding enables you to use `set!` with `vector-ref`. ::: ### vector-least-index +``` scheme +(vector-least-index pred vector [start: 0] [end: #f]) +``` + +Given a predicate `pred` on the elements of given `vector`, that is “increasing”, +i.e. if true for a given element, true on all subsequent elements, and optionally +a `start` (inclusive, defaults to `0`) and an end (exclusive, defaults to `#f` +which designates the vector length), return the least index of a vector element +in the interval [start, env) that satisfies the predicate, or the end if none does. + +::: tip Examples: +``` scheme +> (vector-least-index (cut < 10) #(35 21 16 11 10 9 7 4 1)) +5 +``` +::: ### vector-most-index +``` scheme +(vector-most-index pred vector [start: 0] [end: #f]) +``` + +Given a predicate `pred` on the elements of given `vector`, that is “decreasing”, +i.e. if false for a given element, false on all subsequent elements, and optionally +a `start` (inclusive, defaults to `0`) and an end (exclusive, defaults to `#f` +which designates the vector length), return the most index of a vector element +in the interval [start, env) that satisfies the predicate, or the end if none does. + +::: tip Examples: +``` scheme +> (vector-most-index (cut < 10) #(2 3 5 7 11 13 17 19 23)) +4 +``` +::: ### maybe-subvector diff --git a/src/std/misc/number-test.ss b/src/std/misc/number-test.ss index 2e05b4e11..3e33d137f 100644 --- a/src/std/misc/number-test.ss +++ b/src/std/misc/number-test.ss @@ -189,8 +189,8 @@ (check (least-integer (cut > <> 13.5) 0 20) => 14) (check (least-integer true 0 20) => 0) (check (least-integer false 0 20) => 20) - (check (most-integer true 0 20) => 20) - (check (most-integer false 0 20) => 0) + (check (most-integer true 0 20) => 8) + (check (most-integer false 0 20) => -1) (check (most-integer (cut < <> 13.5) 0 20) => 13)) (test-case "bezout, invert-mod, div-mod, mult-mod" diff --git a/src/std/misc/vector-test.ss b/src/std/misc/vector-test.ss index d22d3507c..0037a3ad1 100644 --- a/src/std/misc/vector-test.ss +++ b/src/std/misc/vector-test.ss @@ -11,8 +11,12 @@ (def foo (vector 1 2 3)) (set! (vector-ref foo 1) 4) (check foo => #(1 4 3)) - (check (vector-least-index (cut < 10) #(35 21 16 11 10 9 7 4 1)) => 5) - (check (vector-most-index (cut < 10) #(2 3 5 7 11 13 17 19 23)) => 4) + (check (vector-least-index (cut < <> 10) #(35 21 16 11 10 9 7 4 1)) => 5) + (check (vector-least-index true #(35 21 16 11 10 9 7 4 1)) => 0) + (check (vector-least-index false #(35 21 16 11 10 9 7 4 1)) => 9) + (check (vector-most-index (cut < <> 10) #(2 3 5 7 11 13 17 19 23)) => 4) + (check (vector-most-index true #(2 3 5 7 11 13 17 19 23)) => 9) + (check (vector-most-index false #(2 3 5 7 11 13 17 19 23)) => 0) (check (maybe-subvector #(1 3 5 7) 2) => #(5 7)) (check (eq? foo (maybe-subvector foo 0 3)) => #t) (check (with-list-builder (c) @@ -22,20 +26,20 @@ (check (with-list-builder (c) (subvector-for-each/index (lambda (x y) (c [x y])) #(a b c d e f g h) start: 5)) - => '((f 5) (g 6) (h 7))) + => '((5 f) (6 g) (7 h))) (check (with-list-builder (c) (subvector-reverse-for-each c #(a b c d e f g h) start: 2 end: 5)) => '(e d c)) (check (with-list-builder (c) (subvector-reverse-for-each/index (lambda (x y) (c [x y])) #(a b c d e f g h) start: 5)) - => '((h 7) (g 6) (f 5))) + => '((7 h) (6 g) (5 f))) (check (subvector->list #(a b c d e f g h) start: 5) - => '(c d e)) + => '(f g h)) (check (cons->vector '(a . b)) => #(a b)) (check (cons->vector 'foo) => #f) (check (vector-filter odd? #(1 2 3 4 5 6 7 8 9) start: 1 end: 7) => #(3 5 7)) - )) + ))) diff --git a/src/std/misc/vector.ss b/src/std/misc/vector.ss index 52e704e33..eeffcd0d9 100644 --- a/src/std/misc/vector.ss +++ b/src/std/misc/vector.ss @@ -39,7 +39,7 @@ ;;; return the most index such that all previous vector elements in the interval [start, env) ;;; satisfy the predicate, or the start if none does. (def (vector-most-index pred? vector start: (start 0) end: (end (vector-length vector))) - (most-integer (lambda (i) (pred? (vector-ref vector i))) start end)) + (least-integer (lambda (i) (not (pred? (vector-ref vector i)))) start end)) ;;; Copy a vector if necessary: return the same if no change in start and end requested. (def (maybe-subvector vector (start 0) (end #f)) @@ -63,9 +63,9 @@ (function (vector-ref vector i))))) (def (subvector-reverse-for-each/index function vector start: (start 0) end: (end #f)) - (def end (or end (vector-length vector))) + (let (end (or end (vector-length vector))) (for ((i (in-iota (- end start) (- end 1) -1))) - (function i (vector-ref vector i)))) + (function i (vector-ref vector i))))) (def (subvector->list vector start: (start 0) end: (end #f)) (with-list-builder (c!) (subvector-for-each c! vector start: start end: end))) @@ -82,5 +82,5 @@ (list->vector (with-list-builder (c) (for (i (in-range start end)) - (def e (vector-ref v i)) - (when (pred? e) (c e)))))) + (let (e (vector-ref v i)) + (when (pred? e) (c e))))))) From 4bd5a7880224bb8fa7593815b7e14813573f13c2 Mon Sep 17 00:00:00 2001 From: Francois-Rene Rideau Date: Sat, 18 Nov 2023 02:39:30 +0000 Subject: [PATCH 4/8] examples --- doc/reference/std/misc/vector.md | 79 +++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/doc/reference/std/misc/vector.md b/doc/reference/std/misc/vector.md index db7e9e516..26dbf93f1 100644 --- a/doc/reference/std/misc/vector.md +++ b/doc/reference/std/misc/vector.md @@ -39,8 +39,12 @@ in the interval [start, env) that satisfies the predicate, or the end if none do ::: tip Examples: ``` scheme -> (vector-least-index (cut < 10) #(35 21 16 11 10 9 7 4 1)) +> (vector-least-index (cut < <> 10) #(35 21 16 11 10 9 7 4 1)) 5 +> (vector-least-index true #(35 21 16 11 10 9 7 4 1)) +0 +> (vector-least-index false #(35 21 16 11 10 9 7 4 1)) +9 ``` ::: @@ -57,23 +61,94 @@ in the interval [start, env) that satisfies the predicate, or the end if none do ::: tip Examples: ``` scheme -> (vector-most-index (cut < 10) #(2 3 5 7 11 13 17 19 23)) +> (vector-most-index (cut < <> 10) #(2 3 5 7 11 13 17 19 23)) 4 +> (vector-most-index true #(2 3 5 7 11 13 17 19 23)) +9 +> (vector-most-index false #(2 3 5 7 11 13 17 19 23)) +0 ``` ::: ### maybe-subvector +::: tip Examples: +``` scheme +> (maybe-subvector #(1 3 5 7) 2) +#(5 7) +> (def foo #(a b c)) +> (eq? foo (maybe-subvector foo 0 3)) +#t +``` +::: + ### subvector-for-each +::: tip Examples: +``` scheme +> (with-list-builder (c) + (subvector-for-each + c #(a b c d e f g h) start: 2 end: 5)) +(c d e) +``` +::: + ### subvector-for-each/index +::: tip Examples: +``` scheme +> (with-list-builder (c) + (subvector-for-each/index + (lambda (x y) (c [x y])) #(a b c d e f g h) start: 5)) +((5 f) (6 g) (7 h)) +``` +::: + ### subvector-reverse-for-each +::: tip Examples: +``` scheme +> (with-list-builder (c) + (subvector-reverse-for-each c #(a b c d e f g h) start: 2 end: 5)) +(e d c) +``` +::: + ### subvector-reverse-for-each/index +::: tip Examples: +``` scheme +> (with-list-builder (c) + (subvector-reverse-for-each/index + (lambda (x y) (c [x y])) #(a b c d e f g h) start: 5)) +((7 h) (6 g) (5 f)) +``` +::: + ### subvector->list +::: tip Examples: +``` scheme +> (subvector->list #(a b c d e f g h) start: 5) +(f g h) +``` +::: + ### cons->vector +::: tip Examples: +``` scheme +> (cons->vector '(a . b)) +#(a b) +> (cons->vector 'foo) +#f +``` +::: + ### vector-filter +::: tip Examples: +``` +> (vector-filter odd? #(1 2 3 4 5 6 7 8 9) start: 1 end: 7) +#(3 5 7) +``` +::: From e9b5fd479e7fc276202dae8626de06fb941f44c7 Mon Sep 17 00:00:00 2001 From: Francois-Rene Rideau Date: Sat, 18 Nov 2023 02:57:46 +0000 Subject: [PATCH 5/8] doc --- doc/reference/std/misc/list-builder.md | 17 ++++++-- doc/reference/std/misc/vector.md | 59 ++++++++++++++++++++------ 2 files changed, 59 insertions(+), 17 deletions(-) diff --git a/doc/reference/std/misc/list-builder.md b/doc/reference/std/misc/list-builder.md index 24e5d58d2..02a1e322b 100644 --- a/doc/reference/std/misc/list-builder.md +++ b/doc/reference/std/misc/list-builder.md @@ -1,8 +1,17 @@ # List builder +The `:std/misc/list-builder` library provides +common bindings for building lists. + +::: tip To use the bindings from this module: +```scheme +(import :std/misc/list-builder) +``` +::: + ## with-list-builder -``` scheme +```scheme (with-list-builder (put! [peek]) body ...) -> list put! := function identifier that modifies internal list @@ -14,7 +23,7 @@ so *put!* and *peek* can be used without wrapping them in a lambda first. `with-list-builder` returns the internal list at the end. ::: tip Examples: -``` scheme +```scheme > (import :std/iter) > (with-list-builder (put!) (for (n (in-iota 100 1)) @@ -30,7 +39,7 @@ so *put!* and *peek* can be used without wrapping them in a lambda first. ## call-with-list-builder -``` scheme +```scheme (call-with-list-builder proc) -> list proc := procedure that takes two proc identifiers as input @@ -49,7 +58,7 @@ with `copy-list` if you want not to be affected by these mutations. Finally, `call-with-list-builder` returns the constructed list. ::: tip Examples: -``` scheme +```scheme > (import :std/iter) > (call-with-list-builder (lambda (put! peek) diff --git a/doc/reference/std/misc/vector.md b/doc/reference/std/misc/vector.md index 26dbf93f1..f8b868e2d 100644 --- a/doc/reference/std/misc/vector.md +++ b/doc/reference/std/misc/vector.md @@ -10,7 +10,7 @@ that complement those provided by RnRS, Gambit and `:std/srfi/43`. ::: ## vector-ref-set! -``` scheme +```scheme (def vector-ref-set! vector-set!) (set! (vector-ref v i) x) ``` @@ -18,7 +18,7 @@ that complement those provided by RnRS, Gambit and `:std/srfi/43`. This binding enables you to use `set!` with `vector-ref`. ::: tip Examples: -``` scheme +```scheme > (def foo (vector 1 2 3)) > (set! (vector-ref foo 1) 4) > foo @@ -27,7 +27,7 @@ This binding enables you to use `set!` with `vector-ref`. ::: ### vector-least-index -``` scheme +```scheme (vector-least-index pred vector [start: 0] [end: #f]) ``` @@ -38,7 +38,7 @@ which designates the vector length), return the least index of a vector element in the interval [start, env) that satisfies the predicate, or the end if none does. ::: tip Examples: -``` scheme +```scheme > (vector-least-index (cut < <> 10) #(35 21 16 11 10 9 7 4 1)) 5 > (vector-least-index true #(35 21 16 11 10 9 7 4 1)) @@ -49,7 +49,7 @@ in the interval [start, env) that satisfies the predicate, or the end if none do ::: ### vector-most-index -``` scheme +```scheme (vector-most-index pred vector [start: 0] [end: #f]) ``` @@ -60,7 +60,7 @@ which designates the vector length), return the most index of a vector element in the interval [start, env) that satisfies the predicate, or the end if none does. ::: tip Examples: -``` scheme +```scheme > (vector-most-index (cut < <> 10) #(2 3 5 7 11 13 17 19 23)) 4 > (vector-most-index true #(2 3 5 7 11 13 17 19 23)) @@ -71,9 +71,15 @@ in the interval [start, env) that satisfies the predicate, or the end if none do ::: ### maybe-subvector +```scheme +(maybe-subvector vector [start 0] [end #f]) => vector +``` + +Copy a vector if necessary: return the same if no change in start and end requested. +(This is unlike `subvector` that always generates a fresh vector.) ::: tip Examples: -``` scheme +```scheme > (maybe-subvector #(1 3 5 7) 2) #(5 7) > (def foo #(a b c)) @@ -83,9 +89,12 @@ in the interval [start, env) that satisfies the predicate, or the end if none do ::: ### subvector-for-each +```scheme +(subvector-for-each function vector start: (start 0) end: (end #f)) +``` ::: tip Examples: -``` scheme +```scheme > (with-list-builder (c) (subvector-for-each c #(a b c d e f g h) start: 2 end: 5)) @@ -94,9 +103,12 @@ in the interval [start, env) that satisfies the predicate, or the end if none do ::: ### subvector-for-each/index +```scheme +(subvector-for-each/index function vector start: (start 0) end: (end #f)) +``` ::: tip Examples: -``` scheme +```scheme > (with-list-builder (c) (subvector-for-each/index (lambda (x y) (c [x y])) #(a b c d e f g h) start: 5)) @@ -105,9 +117,12 @@ in the interval [start, env) that satisfies the predicate, or the end if none do ::: ### subvector-reverse-for-each +```scheme +(subvector-reverse-for-each function vector start: (start 0) end: (end #f)) +``` ::: tip Examples: -``` scheme +```scheme > (with-list-builder (c) (subvector-reverse-for-each c #(a b c d e f g h) start: 2 end: 5)) (e d c) @@ -115,9 +130,12 @@ in the interval [start, env) that satisfies the predicate, or the end if none do ::: ### subvector-reverse-for-each/index +```scheme +(subvector-reverse-for-each/index function vector start: (start 0) end: (end #f)) +``` ::: tip Examples: -``` scheme +```scheme > (with-list-builder (c) (subvector-reverse-for-each/index (lambda (x y) (c [x y])) #(a b c d e f g h) start: 5)) @@ -126,18 +144,24 @@ in the interval [start, env) that satisfies the predicate, or the end if none do ::: ### subvector->list +```scheme +(subvector->list vector start: (start 0) end: (end #f)) +``` ::: tip Examples: -``` scheme +```scheme > (subvector->list #(a b c d e f g h) start: 5) (f g h) ``` ::: ### cons->vector +```scheme +(cons->vector pair) +``` ::: tip Examples: -``` scheme +```scheme > (cons->vector '(a . b)) #(a b) > (cons->vector 'foo) @@ -146,6 +170,15 @@ in the interval [start, env) that satisfies the predicate, or the end if none do ::: ### vector-filter +```scheme +(vector-filter pred? v start: (start 0) end: (end (vector-length v))) +``` + +Filter entries of a vector `v` to those that satisfy the predicate `pred?` +and having indexes between the optional `start` (inclusive, defaults to `0`) +and an end (exclusive, defaults to `#f` which designates the vector length). +Return a fresh vector with the filtered entries. + ::: tip Examples: ``` > (vector-filter odd? #(1 2 3 4 5 6 7 8 9) start: 1 end: 7) From 790502b8f1b2aec7ed8a09a5fe0a3a5842c0a0d3 Mon Sep 17 00:00:00 2001 From: Francois-Rene Rideau Date: Sat, 18 Nov 2023 04:52:36 +0000 Subject: [PATCH 6/8] tests --- src/std/misc/vector-test.ss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/std/misc/vector-test.ss b/src/std/misc/vector-test.ss index 0037a3ad1..8729ddd8c 100644 --- a/src/std/misc/vector-test.ss +++ b/src/std/misc/vector-test.ss @@ -14,9 +14,15 @@ (check (vector-least-index (cut < <> 10) #(35 21 16 11 10 9 7 4 1)) => 5) (check (vector-least-index true #(35 21 16 11 10 9 7 4 1)) => 0) (check (vector-least-index false #(35 21 16 11 10 9 7 4 1)) => 9) + (check (vector-least-index positive? #(-10 -6 -2 -1 0 3 8 19)) => 5) + (check (vector-least-index positive? #(-20 -16 -12 -11 -10 -9 -8 -3)) => 8) + (check (vector-least-index positive? #(3 8 19 23 42 57 83)) => 0) (check (vector-most-index (cut < <> 10) #(2 3 5 7 11 13 17 19 23)) => 4) (check (vector-most-index true #(2 3 5 7 11 13 17 19 23)) => 9) (check (vector-most-index false #(2 3 5 7 11 13 17 19 23)) => 0) + (check (vector-most-index negative? #(-10 -6 -2 -1 0 3 8 19)) => 4) + (check (vector-most-index negative? #(-20 -16 -12 -11 -10 -9 -8 -3)) => 8) + (check (vector-most-index negative? #(3 8 19 23 42 57 83)) => 0) (check (maybe-subvector #(1 3 5 7) 2) => #(5 7)) (check (eq? foo (maybe-subvector foo 0 3)) => #t) (check (with-list-builder (c) From 51b44c9d13c479c7c560e5c6a223c301daf5102b Mon Sep 17 00:00:00 2001 From: Francois-Rene Rideau Date: Sat, 18 Nov 2023 07:23:32 +0000 Subject: [PATCH 7/8] Remove most- tweak docs --- doc/reference/std/misc/number.md | 152 +++++++++++++++++-------------- doc/reference/std/misc/vector.md | 24 +---- src/std/misc/number-test.ss | 7 +- src/std/misc/number.ss | 13 +-- src/std/misc/vector-test.ss | 6 -- src/std/misc/vector.ss | 10 +- 6 files changed, 87 insertions(+), 125 deletions(-) diff --git a/doc/reference/std/misc/number.md b/doc/reference/std/misc/number.md index fa7ae1033..96583d81a 100644 --- a/doc/reference/std/misc/number.md +++ b/doc/reference/std/misc/number.md @@ -1,4 +1,17 @@ -# Extended Real Number Line +# Miscellaneous Number Functions + +This module offers various functions to manipulate numbers +that are not otherwise offered by R7RS, Gambit, +`:std/srfi/141`, `:srfi/144`, or `:srfi/151`. + +::: tip To use the bindings from this module: +```scheme +(import :std/misc/number) +``` +::: + +## Extended Real Number Line + The (affine) extended real number line, where real numbers are enriched with positive and negative infinity, compactifying their order. Positive infinity is represented by IEEE number `+inf.0` while @@ -13,13 +26,32 @@ work better with infinities. Notably: where `(max -inf.0 (+ 1 (expt 2 54)))` returns the rounded `1.8014398509481984e16`. More generally, `xmin` and `xmax` preserve the type of the argument they return. -::: tip To use the bindings from this module: +### real->sign ```scheme -(import :std/misc/number) +(real->sign x) -> -1, 0 or 1 +``` + +Given an extended real number `x`, return an integer, `-1` if the number is negative, +`0` if it is zero, and `1` if it is positive. + +::: tip Examples: +```scheme +> (real->sign 2.7) +1 +> (real->sign 1e-100) +1 +> (real->sign -42) +-1 +> (real->sign -inf.0) +-1 +> (real->sign 0.0) +0 +> (real->sign 0) +0 ``` ::: -## xmin +### xmin ```scheme (xmin ... ) -> real ``` @@ -28,7 +60,7 @@ work better with infinities. Notably: In particular, it returns `+inf.0` (the positive infinity) if provided zero arguments, and is the identity function when given a single argument. -## xmin/list +### xmin/list ```scheme (xmin/list ) -> real ``` @@ -37,7 +69,7 @@ and is the identity function when given a single argument. passed as its arguments. In particular, it returns `+inf.0` (the positive infinity) if provided an empty list. -## xmin! +### xmin! ```scheme (xmin! ...) -> void ``` @@ -45,7 +77,7 @@ infinity) if provided an empty list. `xmin!` side-effects a variable to change it to the `xmin` of the previous value and the provided arguments. -## xmin/map +### xmin/map ```scheme (xmin/map []) -> real ``` @@ -56,7 +88,7 @@ and of a `` real, by default `+inf.0` (the positive infinity). The function is short-circuiting and will not evaluate further values and their side-effects after the bottom value `-inf.0` is reached. -## xmax +### xmax ```scheme (xmax ... ) -> real ``` @@ -65,7 +97,7 @@ after the bottom value `-inf.0` is reached. In particular, it returns `-inf.0` (the negative infinity) if provided zero arguments, and is the identity function when given a single argument. -## xmax/list +### xmax/list ```scheme (xmax/list ) -> real ``` @@ -73,7 +105,7 @@ and is the identity function when given a single argument. `xmax/list` returns the lower bound of the list of extended real arguments passed as its arguments. In particular, it returns `-inf.0` (the negative infinity) if provided an empty list. -## xmax! +### xmax! ```scheme (xmax! ...) -> void ``` @@ -81,7 +113,7 @@ In particular, it returns `-inf.0` (the negative infinity) if provided an empty `xmax!` side-effects a variable to change it to the `xmax` of the previous value and the provided arguments. -## xmax/map +### xmax/map ```scheme (xmax/map []) -> real ``` @@ -92,7 +124,9 @@ and of a `` xreal, by default `-inf.0` (the negative infinity). The function is short-circuiting and will not evaluate further values and their side-effects after the top value `+inf.0` is reached. -## increment!, pre-increment!, post-increment!, decrement!, pre-decrement!, post-decrement! +## Counters + +### increment!, pre-increment!, post-increment!, decrement!, pre-decrement!, post-decrement! ```scheme (increment! place) -> (void) (increment! place increment ...) -> (void) @@ -115,7 +149,7 @@ one (if no further argument is specified) or the provided arguments (if specifie *pre-increment!* and *pre-decrement!* return the value after addition (respectively, subtraction), and *post-increment!* and *post-decrement!* return the value before addition (respectively, subtraction). -## make-counter +### make-counter ```scheme (make-counter) -> counter (make-counter n) -> counter @@ -125,8 +159,11 @@ This function creates a new counter, a function that takes zero or more argument adds the sum of these arguments to the counter (or `1`, not `0`, if no argument was provided), and returns the original value of the counter before the addition (post-increment). You can thus reserve how many entries you are counting before the next call. +Note that this function provides no guarantee of atomicity in case of multithreaded use. + +## Euclidian Division -## integer-part +### integer-part ```scheme (integer-part x) -> integer ``` @@ -168,54 +205,30 @@ if alignment is negative, the roles of `floor-align` and `ceiling-align` are swa ``` ::: -## real->sign -```scheme -(real->sign x) -> -1, 0 or 1 -``` - -Given a real number `x`, return an integer, `-1` if the number is negative, -`0` if it is zero, and `1` if it is positive. +## Natural Numbers -::: tip Examples: -```scheme -> (real->sign 2.7) -1 -> (real->sign 1e-100) -1 -> (real->sign -42) --1 -> (real->sign -inf.0) --1 -> (real->sign 0.0) -0 -> (real->sign 0) -0 -``` -::: - - -## nat? +### nat? ```scheme (nat? x) -> Bool ``` Given any object `x`, return true if it is an non-negative exact integer. -## fxnat? +### fxnat? ```scheme (fxnat? x) -> Bool ``` Given any object `x`, return true if it is an non-negative fixnum. -## nat-below? +### nat-below? ```scheme (nat-below? x end) -> Bool ``` Given any object `x`, return true if it is an non-negative exact integer less than `end` (not included). -## nat-of-length? +### nat-of-length? ```scheme (nat-of-length? x length-in-bits) -> Bool ``` @@ -224,7 +237,7 @@ Given any object `x`, return true if it is an non-negative exact integer that can be stored in `length-in-bits` bits, as witnessed by its `integer-length` being no greater than `length-in-bits` (included). -## integer-of-length? +### integer-of-length? ```scheme (nat-of-length? x length-in-bits) -> Bool ``` @@ -233,7 +246,9 @@ Given any object `x`, return true if it is a (negative, zero or positive) exact integer that can be stored in `length-in-bits` bits, as witnessed by its `integer-length` being strictly less than `length-in-bits` (not included). -## for-each-integer +## Iteration + +### for-each-integer ```scheme (for-each-integer fun from below) ``` @@ -241,14 +256,16 @@ its `integer-length` being strictly less than `length-in-bits` (not included). Given `fun` a function of one argument, call `fun` with each successive increasing integer starting with `from` up to and not including `below`. -## half +## Binary Search (a.k.a. Dichotomy) + +### half ```scheme (half n) ``` Given an integer `n`, return half of `n` if it is even, or half of `n-1` if it is odd. -## least-integer? +### least-integer? ```scheme (least-integer pred? start end) -> integer ``` @@ -259,19 +276,9 @@ it is true for all greater integers in the interval. If no integer in the interval satisfies `pred?`, return `end`. If all do, return `start`. If `pred?` isn't actually increasing, return some integer in the interval. +## Modular Arithmetics -## most-integer? -```scheme -(most-integer pred? start end) -> integer -``` - -Do a binary search in interval (`start`, `end`] to find the most integer for which `pred?` holds, -assuming `pred?` is “decreasing”, i.e. if true for some integer in the interval, -it is true for all lesser integers in the interval. -If no integer in the interval satisfies `pred?`, return `start`. If all do, return `end`. -If `pred?` isn't actually decreasing, return some integer in the interval. - -## bezout +### bezout ```scheme (bezout a b) -> (values integer integer integer) ``` @@ -283,7 +290,7 @@ thus forming a Bezout relationship. Note: the current implementation doesn't use constant-time computations and shouldn't be used for production-grade cryptography. -## mult-mod a b n +### mult-mod a b n ```scheme (mult-mod a b n) -> integer ``` @@ -295,7 +302,7 @@ Note: the current implementation doesn't use constant-time computations and shouldn't be used for production-grade cryptography. Its performance is only moderate. -## invert-mod +### invert-mod ```scheme (invert-mod a n) -> integer ``` @@ -308,7 +315,7 @@ Note: the current implementation doesn't use constant-time computations and shouldn't be used for production-grade cryptography. Its performance is only moderate. -## invert-mod +### invert-mod ```scheme (invert-mod a n) -> integer ``` @@ -322,7 +329,7 @@ Note: the current implementation doesn't use constant-time computations and shouldn't be used for production-grade cryptography. Its performance is only moderate. -## mult-expt-mod +### mult-expt-mod ```scheme (mult-expt-mod a x e n) -> integer ``` @@ -335,7 +342,7 @@ Note: the current implementation doesn't use constant-time computations and shouldn't be used for production-grade cryptography. Its performance is only moderate. -## expt-mod +### expt-mod ```scheme (expt-mod x e n) -> integer ``` @@ -347,24 +354,29 @@ Note: the current implementation doesn't use constant-time computations and shouldn't be used for production-grade cryptography. Its performance is only moderate. -## integer-log +## Logarithms + +### integer-log ```scheme (integer-log a b) -> integer ``` Given two integers `a` and `b`, return the largest natural integer n such that `b**n <= a`. - -## factor-out-powers-of-2 +### factor-out-powers-of-2 ```scheme (factor-out-powers-of-2 n) -> integer ``` -Given an integer `n`, return the smallest integer `m` such that `n = m*2**k` for some integer `k`. +Given an integer `n`, return two values: +the smallest integer `m` such that `n = m*2**k` for some integer `k`, +and the integer `k`. -## factor-out-powers +### factor-out-powers ```scheme (factor-out-powers a b) -> integer ``` -Given integers `a` and `b`, return the smallest integer `m` such that `a = m*b**k` for some integer `k`. +Given integers `a` and `b`, return two values: +the smallest integer `m` such that `a = m*b**k` for some integer `k`, +and the integer `k`. diff --git a/doc/reference/std/misc/vector.md b/doc/reference/std/misc/vector.md index f8b868e2d..210273fee 100644 --- a/doc/reference/std/misc/vector.md +++ b/doc/reference/std/misc/vector.md @@ -1,7 +1,7 @@ # Vector The `:std/misc/vector` library provides common vector functions -that complement those provided by RnRS, Gambit and `:std/srfi/43`. +that complement those provided by RnRS, Gambit, `:std/srfi/43` and `:std/srfi/133`. ::: tip To use the bindings from this module: ```scheme @@ -48,28 +48,6 @@ in the interval [start, env) that satisfies the predicate, or the end if none do ``` ::: -### vector-most-index -```scheme -(vector-most-index pred vector [start: 0] [end: #f]) -``` - -Given a predicate `pred` on the elements of given `vector`, that is “decreasing”, -i.e. if false for a given element, false on all subsequent elements, and optionally -a `start` (inclusive, defaults to `0`) and an end (exclusive, defaults to `#f` -which designates the vector length), return the most index of a vector element -in the interval [start, env) that satisfies the predicate, or the end if none does. - -::: tip Examples: -```scheme -> (vector-most-index (cut < <> 10) #(2 3 5 7 11 13 17 19 23)) -4 -> (vector-most-index true #(2 3 5 7 11 13 17 19 23)) -9 -> (vector-most-index false #(2 3 5 7 11 13 17 19 23)) -0 -``` -::: - ### maybe-subvector ```scheme (maybe-subvector vector [start 0] [end #f]) => vector diff --git a/src/std/misc/number-test.ss b/src/std/misc/number-test.ss index 3e33d137f..e3f925653 100644 --- a/src/std/misc/number-test.ss +++ b/src/std/misc/number-test.ss @@ -185,13 +185,10 @@ (defrule (checks (a b) ...) (begin (check (half a) => b) ...)) (checks (4 2) (3 1) (2 1) (1 0) (0 0) (-1 -1) (-2 -1) (-3 -2) (-4 -2))) - (test-case "least-integer, most integer" + (test-case "least-integer" (check (least-integer (cut > <> 13.5) 0 20) => 14) (check (least-integer true 0 20) => 0) - (check (least-integer false 0 20) => 20) - (check (most-integer true 0 20) => 8) - (check (most-integer false 0 20) => -1) - (check (most-integer (cut < <> 13.5) 0 20) => 13)) + (check (least-integer false 0 20) => 20)) (test-case "bezout, invert-mod, div-mod, mult-mod" (defrule (checks (a b x y d) ...) diff --git a/src/std/misc/number.ss b/src/std/misc/number.ss index 14c77690c..4e8b86e93 100644 --- a/src/std/misc/number.ss +++ b/src/std/misc/number.ss @@ -17,7 +17,7 @@ normalize-nat normalize-integer for-each-integer - half least-integer most-integer + half least-integer bezout invert-mod div-mod mult-mod mult-expt-mod expt-mod integer-log factor-out-powers-of-2 factor-out-powers) @@ -178,17 +178,6 @@ (least-integer pred? start mid) (least-integer pred? (1+ mid) end))))) -;;; Binary search in interval (start, end] to find the most integer i for which pred? holds -;;; for all indexes in [start i), assuming pred? is "decreasing", -;;; i.e. if true for some integer, true for all smaller integers. -;;; If no integer in the interval satisfies pred?, return start. If all do, return end. -(def (most-integer pred? start end) - (if (<= end start) start ; empty interval, return start. - (let (mid (half (+ end start 1))) ;; round up, trust bignums for no overflow - (if (pred? mid) - (most-integer pred? mid end) - (most-integer pred? start (1- mid)))))) - ;; NOTE: the following functions are NOT cryptographic-quality constant-time! ;; Do NOT use them for cryptography in production. ;; TODO: offer an alternate module that offers cryptographic-ready arithmetic primitives via FFI diff --git a/src/std/misc/vector-test.ss b/src/std/misc/vector-test.ss index 8729ddd8c..a378be330 100644 --- a/src/std/misc/vector-test.ss +++ b/src/std/misc/vector-test.ss @@ -17,12 +17,6 @@ (check (vector-least-index positive? #(-10 -6 -2 -1 0 3 8 19)) => 5) (check (vector-least-index positive? #(-20 -16 -12 -11 -10 -9 -8 -3)) => 8) (check (vector-least-index positive? #(3 8 19 23 42 57 83)) => 0) - (check (vector-most-index (cut < <> 10) #(2 3 5 7 11 13 17 19 23)) => 4) - (check (vector-most-index true #(2 3 5 7 11 13 17 19 23)) => 9) - (check (vector-most-index false #(2 3 5 7 11 13 17 19 23)) => 0) - (check (vector-most-index negative? #(-10 -6 -2 -1 0 3 8 19)) => 4) - (check (vector-most-index negative? #(-20 -16 -12 -11 -10 -9 -8 -3)) => 8) - (check (vector-most-index negative? #(3 8 19 23 42 57 83)) => 0) (check (maybe-subvector #(1 3 5 7) 2) => #(5 7)) (check (eq? foo (maybe-subvector foo 0 3)) => #t) (check (with-list-builder (c) diff --git a/src/std/misc/vector.ss b/src/std/misc/vector.ss index eeffcd0d9..e1c2e6f86 100644 --- a/src/std/misc/vector.ss +++ b/src/std/misc/vector.ss @@ -19,7 +19,7 @@ vector-count) srfi43-) (only-in :std/iter for in-range in-iota) (only-in :std/misc/list-builder with-list-builder) - (only-in :std/misc/number least-integer most-integer) + (only-in :std/misc/number least-integer) (only-in :std/sugar while)) ;; Enable (set! (vector-ref v i) x) @@ -33,14 +33,6 @@ (def (vector-least-index pred? vector start: (start 0) end: (end (vector-length vector))) (least-integer (lambda (i) (pred? (vector-ref vector i))) start end)) -;;; Assuming a sorted vector, a predicate on vector elements that is "decreasing", -;;; i.e. if true, true on all preceding elements, and optionally -;;; a start (inclusive, defaults to 0) and an end (exclusive, defaults to vector length), -;;; return the most index such that all previous vector elements in the interval [start, env) -;;; satisfy the predicate, or the start if none does. -(def (vector-most-index pred? vector start: (start 0) end: (end (vector-length vector))) - (least-integer (lambda (i) (not (pred? (vector-ref vector i)))) start end)) - ;;; Copy a vector if necessary: return the same if no change in start and end requested. (def (maybe-subvector vector (start 0) (end #f)) (let* ((len (vector-length vector)) From 6263d1869a30965f68a7b0c758db82ee1ad6650e Mon Sep 17 00:00:00 2001 From: Francois-Rene Rideau Date: Tue, 21 Nov 2023 08:19:17 +0000 Subject: [PATCH 8/8] Make API more uniform --- doc/reference/std/misc/vector.md | 2 +- src/std/misc/vector.ss | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/reference/std/misc/vector.md b/doc/reference/std/misc/vector.md index 210273fee..eec9b260a 100644 --- a/doc/reference/std/misc/vector.md +++ b/doc/reference/std/misc/vector.md @@ -149,7 +149,7 @@ Copy a vector if necessary: return the same if no change in start and end reques ### vector-filter ```scheme -(vector-filter pred? v start: (start 0) end: (end (vector-length v))) +(vector-filter pred? v start: (start 0) end: (end #f)) ``` Filter entries of a vector `v` to those that satisfy the predicate `pred?` diff --git a/src/std/misc/vector.ss b/src/std/misc/vector.ss index e1c2e6f86..2849449ee 100644 --- a/src/std/misc/vector.ss +++ b/src/std/misc/vector.ss @@ -30,8 +30,8 @@ ;;; a start (inclusive, defaults to 0) and an end (exclusive, defaults to vector length), ;;; return the least index of a vector element in the interval [start, env) ;;; that satisfies the predicate, or the end if none does. -(def (vector-least-index pred? vector start: (start 0) end: (end (vector-length vector))) - (least-integer (lambda (i) (pred? (vector-ref vector i))) start end)) +(def (vector-least-index pred? vector start: (start 0) end: (end #f)) + (least-integer (lambda (i) (pred? (vector-ref vector i))) start (or end (vector-length vector)))) ;;; Copy a vector if necessary: return the same if no change in start and end requested. (def (maybe-subvector vector (start 0) (end #f)) @@ -70,9 +70,9 @@ ;; (vector-set! vector index (fun (vector-ref vector index)))) ;; Filter entries of a vector to those that satisfy the predicate -(def (vector-filter pred? v start: (start 0) end: (end (vector-length v))) +(def (vector-filter pred? v start: (start 0) end: (end #f)) (list->vector (with-list-builder (c) - (for (i (in-range start end)) + (for (i (in-range start (or end (vector-length v)))) (let (e (vector-ref v i)) (when (pred? e) (c e)))))))