Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion racket/prologos/lib/prologos/core/pvec.prologos
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require [prologos::core::collection-traits :refer [Seqable
Functor]]
require [prologos::data::lseq :refer [LSeq]]
require [prologos::data::lseq-ops :refer [list-to-lseq lseq-to-list]]
require [prologos::data::list :refer [List nil cons]]
require [prologos::data::list :refer [List nil cons zip-with]]
require [prologos::data::option :refer [Option some none]]
require [prologos::data::nat :refer [lt?]]

Expand Down Expand Up @@ -134,3 +134,15 @@ defn pvec-from-list-fn [xs]
spec pvec-to-list-fn {A : Type} [PVec A] -> [List A]
defn pvec-to-list-fn [v]
pvec-to-list v

;; ========================================
;; pvec-zip-with : combine two PVecs element-wise
;; ========================================
;; Signature mirrors List zip-with; truncates to the shorter PVec's length.
;; Implementation: convert both pvecs to lists, zip-with on lists, convert
;; the result back to a PVec. The asymptotic cost is the same as a direct
;; index walk: O(n) for the dominant length.
spec pvec-zip-with {A B C : Type} [A -> B -> C] [PVec A] [PVec B] -> [PVec C]
:doc "Combine two PVecs element-wise with a function; truncates to shorter."
defn pvec-zip-with [f xs ys]
pvec-from-list [zip-with f [pvec-to-list xs] [pvec-to-list ys]]
179 changes: 179 additions & 0 deletions racket/prologos/tests/test-pvec-zip-with.rkt
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
#lang racket/base

;;;
;;; Tests for pvec-zip-with library function.
;;; Addresses eigentrust pitfalls doc #13: provide a zip-with equivalent for
;;; PVec so elementwise operations on two PVecs do not require explicit
;;; index-threaded-accumulator recursion in user code.
;;;
;;; Signature: pvec-zip-with : (A -> B -> C) -> PVec A -> PVec B -> PVec C
;;; Truncates to the length of the shorter input (mirrors List zip-with).
;;;

(require rackunit
racket/list
racket/string
"test-support.rkt"
"../macros.rkt"
"../syntax.rkt"
"../errors.rkt"
"../metavar-store.rkt"
"../global-env.rkt"
"../driver.rkt"
"../namespace.rkt"
"../multi-dispatch.rkt")

;; ========================================
;; Helpers
;; ========================================

(define (run-ns s)
(parameterize ([current-prelude-env (hasheq)]
[current-module-definitions-content (hasheq)]
[current-ns-context #f]
[current-module-registry prelude-module-registry]
[current-lib-paths (list prelude-lib-dir)]
[current-mult-meta-store (make-hasheq)]
[current-preparse-registry prelude-preparse-registry]
[current-ctor-registry (current-ctor-registry)]
[current-type-meta (current-type-meta)]
[current-trait-registry prelude-trait-registry]
[current-impl-registry prelude-impl-registry]
[current-param-impl-registry prelude-param-impl-registry]
[current-multi-defn-registry (current-multi-defn-registry)]
[current-spec-store (hasheq)])
(install-module-loader!)
(process-string s)))

(define (run-last s)
(last (run-ns s)))

(define (check-contains actual substr [msg #f])
(check-true (and (string? actual) (string-contains? actual substr))
(or msg (format "Expected ~s to contain ~s" actual substr))))

(define preamble
(string-append
"(ns pvec-zip-with-test)\n"
"(require [prologos::core::pvec :refer [pvec-zip-with]])\n"))

;; ========================================
;; 1. Equal-length pvecs (basic case)
;; ========================================

(test-case "pvec-zip-with/equal-length: type-checks against PVec Nat"
(define result
(run-last
(string-append preamble
"(check (pvec-zip-with add @[1N 2N 3N] @[10N 20N 30N]) : (PVec Nat))\n")))
(check-equal? result "OK"))

(test-case "pvec-zip-with/equal-length: elementwise add yields expected length"
;; @[1+10, 2+20, 3+30] = @[11, 22, 33]; length 3
(define result
(run-last
(string-append preamble
"(eval (pvec-length"
" (pvec-zip-with add @[1N 2N 3N] @[10N 20N 30N])))\n")))
(check-equal? result "3N : Nat"))

(test-case "pvec-zip-with/equal-length: elementwise add nth=0 yields 11"
(define result
(run-last
(string-append preamble
"(eval (pvec-nth"
" (pvec-zip-with add @[1N 2N 3N] @[10N 20N 30N])"
" zero))\n")))
(check-equal? result "11N : Nat"))

(test-case "pvec-zip-with/equal-length: elementwise add nth=2 yields 33"
(define result
(run-last
(string-append preamble
"(eval (pvec-nth"
" (pvec-zip-with add @[1N 2N 3N] @[10N 20N 30N])"
" (suc (suc zero))))\n")))
(check-equal? result "33N : Nat"))

;; ========================================
;; 2. Truncation: shorter-first (xs shorter)
;; ========================================

(test-case "pvec-zip-with/truncate-xs-shorter: length is min(2,4)=2"
(define result
(run-last
(string-append preamble
"(eval (pvec-length"
" (pvec-zip-with add @[1N 2N] @[10N 20N 30N 40N])))\n")))
(check-equal? result "2N : Nat"))

;; ========================================
;; 3. Truncation: shorter-second (ys shorter)
;; ========================================

(test-case "pvec-zip-with/truncate-ys-shorter: length is min(4,1)=1"
(define result
(run-last
(string-append preamble
"(eval (pvec-length"
" (pvec-zip-with add @[1N 2N 3N 4N] @[100N])))\n")))
(check-equal? result "1N : Nat"))

(test-case "pvec-zip-with/truncate-ys-shorter: nth=0 yields 1+100=101"
(define result
(run-last
(string-append preamble
"(eval (pvec-nth"
" (pvec-zip-with add @[1N 2N 3N 4N] @[100N])"
" zero))\n")))
(check-equal? result "101N : Nat"))

;; ========================================
;; 4. Empty inputs
;; ========================================

(test-case "pvec-zip-with/both-empty: yields empty PVec (length 0)"
(define result
(run-last
(string-append preamble
"(check (pvec-zip-with add @[] @[]) : (PVec Nat))\n")))
(check-equal? result "OK"))

(test-case "pvec-zip-with/xs-empty: yields empty PVec (length 0)"
(define result
(run-last
(string-append preamble
"(eval (pvec-length"
" (pvec-zip-with add @[] @[1N 2N 3N])))\n")))
(check-equal? result "0N : Nat"))

;; ========================================
;; 5. Heterogeneous: f changes element type (Nat -> Nat -> Bool)
;; ========================================

(test-case "pvec-zip-with/result-type: PVec Bool when f returns Bool"
;; (fn x y -> zero? x) :: Nat -> Nat -> Bool
(define result
(run-last
(string-append preamble
"(check (pvec-zip-with"
" (fn (x : Nat) (y : Nat) (zero? x))"
" @[0N 1N]"
" @[10N 20N]) : (PVec Bool))\n")))
(check-equal? result "OK"))

;; ========================================
;; 6. Eigentrust use case mirror: pvec-zip-with with named binary op
;; ========================================
;; The eigentrust pitfalls doc's motivating example was elementwise addition over
;; two reputation vectors. Verify that pattern works end-to-end.

(test-case "pvec-zip-with/eigentrust-mirror: add two equal-length vectors"
(define result
(run-last
(string-append preamble
"(eval (pvec-to-list"
" (pvec-zip-with add @[5N 7N 11N] @[2N 3N 4N])))\n")))
(check-contains result "7N") ; 5 + 2
(check-contains result "10N") ; 7 + 3
(check-contains result "15N")) ; 11 + 4
Loading