-
Notifications
You must be signed in to change notification settings - Fork 13
/
vec.cljc
149 lines (127 loc) · 5.09 KB
/
vec.cljc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
; Copyright (c) Alan Thompson. All rights reserved.
; The use and distribution terms for this software are covered by the Eclipse Public License 1.0
; (http://opensource.org/licenses/eclipse-1.0.php) which can be found in the file epl-v10.html at
; the root of this distribution. By using this software in any fashion, you are agreeing to be
; bound by the terms of this license. You must not remove this notice, or any other, from this
; software.
(ns tupelo.vec
(:refer-clojure :exclude [load ->VecNode get set])
#?(:clj (:require
[clojure.core :as cc]
[clojure.set :as set]
[schema.core :as s]
[tupelo.core :as t :refer [spy spyx spyxx spyx-pretty grab glue map-entry indexed
forv vals->map fetch-in let-spy xlast xfirst keep-if drop-if
it-> cond-it-> xfirst xsecond xthird xlast ]]
[tupelo.data.index :as index]
[tupelo.schema :as tsk]
))
#?(:cljs (:require
[clojure.core :as cc]
[clojure.set :as set]
[schema.core :as s]
[tupelo.core :as t :include-macros true
:refer [spy spyx spyxx spyx-pretty grab glue map-entry indexed
forv vals->map fetch-in let-spy xlast xfirst keep-if drop-if
it-> cond-it-> xfirst xsecond xthird xlast
]
] ; #todo :include-macros true
[tupelo.data.index :as index]
[tupelo.schema :as tsk]
)))
; #todo add indexes
; #todo add sets (primative only or EID) => map with same key/value
; #todo copy destruct syntax for search
#?(:cljs (enable-console-print!))
(s/defn coerce->vec :- tsk/Vec
"Coerce any scalar values to a vector"
[arg]
(if (sequential? arg)
(vec arg)
[arg]))
(s/defn validate-indexes-complete :- s/Any ; #todo maybe => tupelo.data.indexing
"Validates that a collection of N index values includes all values in [0..N)."
[idxs]
(let [expected-set (cc/set (range (count idxs)))
actual-set (cc/set idxs)]
(assert (= expected-set actual-set)))
idxs)
(s/defn verify-idxs
"Assets that an integer index is non-negative and less than a bound"
[tgt :- tsk/Vec
idxs :- [s/Int]]
(let [bound (count tgt)]
(doseq [idx idxs]
(assert (and (int? idx)
(<= 0 idx) (< idx bound)))))
idxs)
(defn pred-index
"Given a predicate fn and a collection of values, returns the index values for which the
predicate is true & false like:
(pred-index #(zero? (rem % 3)) [0 10 20 30 40 50 60 70 80])
=> {:idxs-true [0 3 6]
:idxs-false [1 2 4 5 7 8] } "
[pred coll]
(let [pred-indexed (indexed (mapv pred coll))
grouped (glue {true [] false []} ; ensure both t/f are present
(group-by xsecond pred-indexed))
result {:idxs-true (it-> grouped
(grab true it)
(mapv xfirst it))
:idxs-false (it-> grouped
(grab false it)
(mapv xfirst it))}]
result))
; #todo maybe (set-idx dst idxs src) ? (<dest> <bridge> <src>)
(def IndexSpec
(s/cond-pre [s/Int] s/Int) )
(s/defn get :- tsk/Vec
"Given a source vector V and a list of index values `idx`, returns a vector: [ V(idx-1) V(idx-2) ...]"
[src :- tsk/Vec
idxs :- IndexSpec]
(let [idxs (coerce->vec idxs)
bound-src (count src)]
(verify-idxs src idxs)
(forv [idx idxs]
(nth src idx))))
(s/defn set :- tsk/Vec
"Given a dest vector V, a list of index values `idx`, and a conforming src vector,
returns a modified V such that V(idx-j) = src[j] for j in [0..len(idx)] "
[dest :- tsk/Vec
idxs :- IndexSpec
src :- tsk/Vec]
(let [idxs (coerce->vec idxs)
src (coerce->vec src)]
(verify-idxs dest idxs)
(assert (= (count idxs) (count src)))
(loop [result (transient dest)
idxs-vals idxs
elems src]
(if (empty? idxs-vals)
(persistent! result)
(let [result-next (assoc! result (first idxs-vals) (first elems))]
(recur result-next
(rest idxs-vals)
(rest elems)))))))
(s/defn set-lax :- tsk/Vec
"Like vec-put-idxs, but is lax in accepting the src vector. If a scalar is supplied,
it is replaced with `(repeat <src>)`. If the src vector is longer than the idxs, it is truncated
to conform."
[dest :- tsk/Vec
idxs :- IndexSpec
src :- s/Any]
(let [idxs-len (count idxs)
src-use (cond
(not (sequential? src)) (repeat idxs-len src)
(< idxs-len (count src)) (t/xtake idxs-len src)
:else src)]
(set dest idxs src-use)))
(s/defn del :- tsk/Vec
"Delete elements at the specified indexes."
[src :- tsk/Vec
idxs :- IndexSpec]
(let [drop-idx? (t/->set (coerce->vec idxs))
idxs-keep (drop-if drop-idx? (range (count src)))]
(verify-idxs src idxs)
(forv [idx idxs-keep]
(nth src idx))))