Compare commits
1 Commits
loops/ocam
...
f13e03e625
| Author | SHA1 | Date | |
|---|---|---|---|
| f13e03e625 |
@@ -25,9 +25,8 @@
|
||||
; Glyph classification sets
|
||||
; ============================================================
|
||||
|
||||
(define
|
||||
apl-parse-op-glyphs
|
||||
(list "/" "⌿" "\\" "⍀" "¨" "⍨" "∘" "." "⍣" "⍤" "⍥" "@"))
|
||||
(define apl-parse-op-glyphs
|
||||
(list "/" "\\" "¨" "⍨" "∘" "." "⍣" "⍤" "⍥" "@"))
|
||||
|
||||
(define
|
||||
apl-parse-fn-glyphs
|
||||
@@ -83,48 +82,22 @@
|
||||
"⍎"
|
||||
"⍕"))
|
||||
|
||||
(define apl-quad-fn-names (list "⎕FMT" "⎕←"))
|
||||
(define apl-quad-fn-names (list "⎕FMT"))
|
||||
|
||||
(define apl-known-fn-names (list))
|
||||
(define
|
||||
apl-parse-op-glyph?
|
||||
(fn (v) (some (fn (g) (= g v)) apl-parse-op-glyphs)))
|
||||
|
||||
; ============================================================
|
||||
; Token accessors
|
||||
; ============================================================
|
||||
|
||||
(define
|
||||
apl-collect-fn-bindings
|
||||
(fn
|
||||
(stmt-groups)
|
||||
(set! apl-known-fn-names (list))
|
||||
(for-each
|
||||
(fn
|
||||
(toks)
|
||||
(when
|
||||
(and
|
||||
(>= (len toks) 3)
|
||||
(= (tok-type (nth toks 0)) :name)
|
||||
(= (tok-type (nth toks 1)) :assign)
|
||||
(= (tok-type (nth toks 2)) :lbrace))
|
||||
(set!
|
||||
apl-known-fn-names
|
||||
(cons (tok-val (nth toks 0)) apl-known-fn-names))))
|
||||
stmt-groups)))
|
||||
|
||||
(define
|
||||
apl-parse-op-glyph?
|
||||
(fn (v) (some (fn (g) (= g v)) apl-parse-op-glyphs)))
|
||||
|
||||
(define
|
||||
apl-parse-fn-glyph?
|
||||
(fn (v) (some (fn (g) (= g v)) apl-parse-fn-glyphs)))
|
||||
|
||||
(define tok-type (fn (tok) (get tok :type)))
|
||||
|
||||
; ============================================================
|
||||
; Collect trailing operators starting at index i
|
||||
; Returns {:ops (op ...) :end new-i}
|
||||
; ============================================================
|
||||
|
||||
(define tok-val (fn (tok) (get tok :value)))
|
||||
|
||||
(define
|
||||
@@ -134,8 +107,8 @@
|
||||
(and (= (tok-type tok) :glyph) (apl-parse-op-glyph? (tok-val tok)))))
|
||||
|
||||
; ============================================================
|
||||
; Build a derived-fn node by chaining operators left-to-right
|
||||
; (+/¨ → (:derived-fn "¨" (:derived-fn "/" (:fn-glyph "+"))))
|
||||
; Collect trailing operators starting at index i
|
||||
; Returns {:ops (op ...) :end new-i}
|
||||
; ============================================================
|
||||
|
||||
(define
|
||||
@@ -146,17 +119,15 @@
|
||||
(and (= (tok-type tok) :glyph) (apl-parse-fn-glyph? (tok-val tok)))
|
||||
(and
|
||||
(= (tok-type tok) :name)
|
||||
(or
|
||||
(some (fn (q) (= q (tok-val tok))) apl-quad-fn-names)
|
||||
(some (fn (q) (= q (tok-val tok))) apl-known-fn-names))))))
|
||||
|
||||
; ============================================================
|
||||
; Find matching close bracket/paren/brace
|
||||
; Returns the index of the matching close token
|
||||
; ============================================================
|
||||
(some (fn (q) (= q (tok-val tok))) apl-quad-fn-names)))))
|
||||
|
||||
(define collect-ops (fn (tokens i) (collect-ops-loop tokens i (list))))
|
||||
|
||||
; ============================================================
|
||||
; Build a derived-fn node by chaining operators left-to-right
|
||||
; (+/¨ → (:derived-fn "¨" (:derived-fn "/" (:fn-glyph "+"))))
|
||||
; ============================================================
|
||||
|
||||
(define
|
||||
collect-ops-loop
|
||||
(fn
|
||||
@@ -172,10 +143,8 @@
|
||||
{:end i :ops acc})))))
|
||||
|
||||
; ============================================================
|
||||
; Segment collection: scan tokens left-to-right, building
|
||||
; a list of {:kind "val"/"fn" :node ast} segments.
|
||||
; Operators following function glyphs are merged into
|
||||
; derived-fn nodes during this pass.
|
||||
; Find matching close bracket/paren/brace
|
||||
; Returns the index of the matching close token
|
||||
; ============================================================
|
||||
|
||||
(define
|
||||
@@ -194,20 +163,12 @@
|
||||
(find-matching-close-loop tokens start open-type close-type 1)))
|
||||
|
||||
; ============================================================
|
||||
; Build tree from segment list
|
||||
;
|
||||
; The segments are in left-to-right order.
|
||||
; APL evaluates right-to-left, so the LEFTMOST function is
|
||||
; the outermost (last-evaluated) node.
|
||||
;
|
||||
; Patterns:
|
||||
; [val] → val node
|
||||
; [fn val ...] → (:monad fn (build-tree rest))
|
||||
; [val fn val ...] → (:dyad fn val (build-tree rest))
|
||||
; [val val ...] → (:vec val1 val2 ...) — strand
|
||||
; Segment collection: scan tokens left-to-right, building
|
||||
; a list of {:kind "val"/"fn" :node ast} segments.
|
||||
; Operators following function glyphs are merged into
|
||||
; derived-fn nodes during this pass.
|
||||
; ============================================================
|
||||
|
||||
; Find the index of the first function segment (returns -1 if none)
|
||||
(define
|
||||
find-matching-close-loop
|
||||
(fn
|
||||
@@ -247,9 +208,21 @@
|
||||
collect-segments
|
||||
(fn (tokens) (collect-segments-loop tokens 0 (list))))
|
||||
|
||||
; Build an array node from 0..n value segments
|
||||
; If n=1 → return that segment's node
|
||||
; If n>1 → return (:vec node1 node2 ...)
|
||||
; ============================================================
|
||||
; Build tree from segment list
|
||||
;
|
||||
; The segments are in left-to-right order.
|
||||
; APL evaluates right-to-left, so the LEFTMOST function is
|
||||
; the outermost (last-evaluated) node.
|
||||
;
|
||||
; Patterns:
|
||||
; [val] → val node
|
||||
; [fn val ...] → (:monad fn (build-tree rest))
|
||||
; [val fn val ...] → (:dyad fn val (build-tree rest))
|
||||
; [val val ...] → (:vec val1 val2 ...) — strand
|
||||
; ============================================================
|
||||
|
||||
; Find the index of the first function segment (returns -1 if none)
|
||||
(define
|
||||
collect-segments-loop
|
||||
(fn
|
||||
@@ -269,62 +242,36 @@
|
||||
((= tt :str)
|
||||
(collect-segments-loop tokens (+ i 1) (append acc {:kind "val" :node (list :str tv)})))
|
||||
((= tt :name)
|
||||
(cond
|
||||
((some (fn (q) (= q tv)) apl-quad-fn-names)
|
||||
(if
|
||||
(some (fn (q) (= q tv)) apl-quad-fn-names)
|
||||
(let
|
||||
((op-result (collect-ops tokens (+ i 1))))
|
||||
(let
|
||||
((ops (get op-result :ops))
|
||||
(ni (get op-result :end)))
|
||||
((ops (get op-result :ops)) (ni (get op-result :end)))
|
||||
(let
|
||||
((fn-node (build-derived-fn (list :fn-glyph tv) ops)))
|
||||
(collect-segments-loop
|
||||
tokens
|
||||
ni
|
||||
(append acc {:kind "fn" :node fn-node}))))))
|
||||
((some (fn (q) (= q tv)) apl-known-fn-names)
|
||||
(let
|
||||
((op-result (collect-ops tokens (+ i 1))))
|
||||
(let
|
||||
((ops (get op-result :ops))
|
||||
(ni (get op-result :end)))
|
||||
(let
|
||||
((fn-node (build-derived-fn (list :fn-name tv) ops)))
|
||||
(collect-segments-loop
|
||||
tokens
|
||||
ni
|
||||
(append acc {:kind "fn" :node fn-node}))))))
|
||||
(else
|
||||
(append acc {:kind "fn" :node fn-node})))))
|
||||
(let
|
||||
((br (maybe-bracket (list :name tv) tokens (+ i 1))))
|
||||
(collect-segments-loop
|
||||
tokens
|
||||
(nth br 1)
|
||||
(append acc {:kind "val" :node (nth br 0)}))))))
|
||||
(append acc {:kind "val" :node (nth br 0)})))))
|
||||
((= tt :lparen)
|
||||
(let
|
||||
((end (find-matching-close tokens (+ i 1) :lparen :rparen)))
|
||||
(let
|
||||
((inner-tokens (slice tokens (+ i 1) end))
|
||||
(after (+ end 1)))
|
||||
(let
|
||||
((inner-segs (collect-segments inner-tokens)))
|
||||
(if
|
||||
(and
|
||||
(>= (len inner-segs) 2)
|
||||
(every? (fn (s) (= (get s :kind) "fn")) inner-segs))
|
||||
(let
|
||||
((train-node (cons :train (map (fn (s) (get s :node)) inner-segs))))
|
||||
(collect-segments-loop
|
||||
tokens
|
||||
after
|
||||
(append acc {:kind "fn" :node train-node})))
|
||||
(let
|
||||
((br (maybe-bracket (parse-apl-expr inner-tokens) tokens after)))
|
||||
(collect-segments-loop
|
||||
tokens
|
||||
(nth br 1)
|
||||
(append acc {:kind "val" :node (nth br 0)}))))))))
|
||||
(append acc {:kind "val" :node (nth br 0)}))))))
|
||||
((= tt :lbrace)
|
||||
(let
|
||||
((end (find-matching-close tokens (+ i 1) :lbrace :rbrace)))
|
||||
@@ -399,12 +346,9 @@
|
||||
|
||||
(define find-first-fn (fn (segs) (find-first-fn-loop segs 0)))
|
||||
|
||||
|
||||
; ============================================================
|
||||
; Split token list on statement separators (diamond / newline)
|
||||
; Only splits at depth 0 (ignores separators inside { } or ( ) )
|
||||
; ============================================================
|
||||
|
||||
; Build an array node from 0..n value segments
|
||||
; If n=1 → return that segment's node
|
||||
; If n>1 → return (:vec node1 node2 ...)
|
||||
(define
|
||||
find-first-fn-loop
|
||||
(fn
|
||||
@@ -426,9 +370,10 @@
|
||||
(get (first segs) :node)
|
||||
(cons :vec (map (fn (s) (get s :node)) segs)))))
|
||||
|
||||
|
||||
; ============================================================
|
||||
; Parse a dfn body (tokens between { and })
|
||||
; Handles guard expressions: cond : expr
|
||||
; Split token list on statement separators (diamond / newline)
|
||||
; Only splits at depth 0 (ignores separators inside { } or ( ) )
|
||||
; ============================================================
|
||||
|
||||
(define
|
||||
@@ -463,6 +408,11 @@
|
||||
split-statements
|
||||
(fn (tokens) (split-statements-loop tokens (list) (list) 0)))
|
||||
|
||||
; ============================================================
|
||||
; Parse a dfn body (tokens between { and })
|
||||
; Handles guard expressions: cond : expr
|
||||
; ============================================================
|
||||
|
||||
(define
|
||||
split-statements-loop
|
||||
(fn
|
||||
@@ -517,10 +467,6 @@
|
||||
((stmt-groups (split-statements tokens)))
|
||||
(let ((stmts (map parse-dfn-stmt stmt-groups))) (cons :dfn stmts)))))
|
||||
|
||||
; ============================================================
|
||||
; Parse a single statement (assignment or expression)
|
||||
; ============================================================
|
||||
|
||||
(define
|
||||
parse-dfn-stmt
|
||||
(fn
|
||||
@@ -537,17 +483,12 @@
|
||||
(parse-apl-expr body-tokens)))
|
||||
(parse-stmt tokens)))))
|
||||
|
||||
; ============================================================
|
||||
; Parse an expression from a flat token list
|
||||
; ============================================================
|
||||
|
||||
(define
|
||||
find-top-level-colon
|
||||
(fn (tokens i) (find-top-level-colon-loop tokens i 0)))
|
||||
|
||||
; ============================================================
|
||||
; Main entry point
|
||||
; parse-apl: string → AST
|
||||
; Parse a single statement (assignment or expression)
|
||||
; ============================================================
|
||||
|
||||
(define
|
||||
@@ -567,6 +508,10 @@
|
||||
((and (= tt :colon) (= depth 0)) i)
|
||||
(true (find-top-level-colon-loop tokens (+ i 1) depth)))))))
|
||||
|
||||
; ============================================================
|
||||
; Parse an expression from a flat token list
|
||||
; ============================================================
|
||||
|
||||
(define
|
||||
parse-stmt
|
||||
(fn
|
||||
@@ -581,6 +526,11 @@
|
||||
(parse-apl-expr (slice tokens 2)))
|
||||
(parse-apl-expr tokens))))
|
||||
|
||||
; ============================================================
|
||||
; Main entry point
|
||||
; parse-apl: string → AST
|
||||
; ============================================================
|
||||
|
||||
(define
|
||||
parse-apl-expr
|
||||
(fn
|
||||
@@ -597,52 +547,13 @@
|
||||
((tokens (apl-tokenize src)))
|
||||
(let
|
||||
((stmt-groups (split-statements tokens)))
|
||||
(begin
|
||||
(apl-collect-fn-bindings stmt-groups)
|
||||
(if
|
||||
(= (len stmt-groups) 0)
|
||||
nil
|
||||
(if
|
||||
(= (len stmt-groups) 1)
|
||||
(parse-stmt (first stmt-groups))
|
||||
(cons :program (map parse-stmt stmt-groups)))))))))
|
||||
|
||||
(define
|
||||
split-bracket-loop
|
||||
(fn
|
||||
(tokens current acc depth)
|
||||
(if
|
||||
(= (len tokens) 0)
|
||||
(append acc (list current))
|
||||
(let
|
||||
((tok (first tokens)) (more (rest tokens)))
|
||||
(let
|
||||
((tt (tok-type tok)))
|
||||
(cond
|
||||
((or (= tt :lparen) (= tt :lbrace) (= tt :lbracket))
|
||||
(split-bracket-loop
|
||||
more
|
||||
(append current (list tok))
|
||||
acc
|
||||
(+ depth 1)))
|
||||
((or (= tt :rparen) (= tt :rbrace) (= tt :rbracket))
|
||||
(split-bracket-loop
|
||||
more
|
||||
(append current (list tok))
|
||||
acc
|
||||
(- depth 1)))
|
||||
((and (= tt :semi) (= depth 0))
|
||||
(split-bracket-loop
|
||||
more
|
||||
(list)
|
||||
(append acc (list current))
|
||||
depth))
|
||||
(else
|
||||
(split-bracket-loop more (append current (list tok)) acc depth))))))))
|
||||
|
||||
(define
|
||||
split-bracket-content
|
||||
(fn (tokens) (split-bracket-loop tokens (list) (list) 0)))
|
||||
(cons :program (map parse-stmt stmt-groups))))))))
|
||||
|
||||
(define
|
||||
maybe-bracket
|
||||
@@ -657,18 +568,9 @@
|
||||
(let
|
||||
((inner-tokens (slice tokens (+ after 1) end))
|
||||
(next-after (+ end 1)))
|
||||
(let
|
||||
((sections (split-bracket-content inner-tokens)))
|
||||
(if
|
||||
(= (len sections) 1)
|
||||
(let
|
||||
((idx-expr (parse-apl-expr inner-tokens)))
|
||||
(let
|
||||
((indexed (list :dyad (list :fn-glyph "⌷") idx-expr val-node)))
|
||||
(maybe-bracket indexed tokens next-after)))
|
||||
(let
|
||||
((axis-exprs (map (fn (toks) (if (= (len toks) 0) :all (parse-apl-expr toks))) sections)))
|
||||
(let
|
||||
((indexed (cons :bracket (cons val-node axis-exprs))))
|
||||
(maybe-bracket indexed tokens next-after)))))))
|
||||
(maybe-bracket indexed tokens next-after)))))
|
||||
(list val-node after))))
|
||||
|
||||
@@ -883,7 +883,7 @@
|
||||
(let
|
||||
((sub (apl-permutations (- n 1))))
|
||||
(reduce
|
||||
(fn (acc p) (append (apl-insert-everywhere n p) acc))
|
||||
(fn (acc p) (append acc (apl-insert-everywhere n p)))
|
||||
(list)
|
||||
sub)))))
|
||||
|
||||
@@ -985,38 +985,6 @@
|
||||
(some (fn (c) (= c 0)) codes)
|
||||
(some (fn (c) (= c (nth e 1))) codes)))))
|
||||
|
||||
(define
|
||||
apl-cartesian
|
||||
(fn
|
||||
(lists)
|
||||
(if
|
||||
(= (len lists) 0)
|
||||
(list (list))
|
||||
(let
|
||||
((rest-prods (apl-cartesian (rest lists))))
|
||||
(reduce
|
||||
(fn (acc x) (append acc (map (fn (p) (cons x p)) rest-prods)))
|
||||
(list)
|
||||
(first lists))))))
|
||||
|
||||
(define
|
||||
apl-bracket-multi
|
||||
(fn
|
||||
(axes arr)
|
||||
(let
|
||||
((shape (get arr :shape)) (ravel (get arr :ravel)))
|
||||
(let
|
||||
((rank (len shape)) (strides (apl-strides shape)))
|
||||
(let
|
||||
((axis-info (map (fn (i) (let ((a (nth axes i))) (cond ((= a nil) {:idxs (range 0 (nth shape i)) :scalar? false}) ((= (len (get a :shape)) 0) {:idxs (list (- (first (get a :ravel)) apl-io)) :scalar? true}) (else {:idxs (map (fn (x) (- x apl-io)) (get a :ravel)) :scalar? false})))) (range 0 rank))))
|
||||
(let
|
||||
((cells (apl-cartesian (map (fn (a) (get a :idxs)) axis-info))))
|
||||
(let
|
||||
((result-ravel (map (fn (cell) (let ((flat (reduce + 0 (map (fn (i) (* (nth cell i) (nth strides i))) (range 0 rank))))) (nth ravel flat))) cells)))
|
||||
(let
|
||||
((result-shape (filter (fn (x) (>= x 0)) (map (fn (i) (let ((a (nth axis-info i))) (if (get a :scalar?) -1 (len (get a :idxs))))) (range 0 rank)))))
|
||||
(make-array result-shape result-ravel)))))))))
|
||||
|
||||
(define
|
||||
apl-reduce
|
||||
(fn
|
||||
|
||||
@@ -39,7 +39,6 @@ cat > "$TMPFILE" << 'EPOCHS'
|
||||
(load "lib/apl/tests/idioms.sx")
|
||||
(load "lib/apl/tests/eval-ops.sx")
|
||||
(load "lib/apl/tests/pipeline.sx")
|
||||
(load "lib/apl/tests/programs-e2e.sx")
|
||||
(epoch 4)
|
||||
(eval "(list apl-test-pass apl-test-fail)")
|
||||
EPOCHS
|
||||
|
||||
@@ -178,137 +178,3 @@
|
||||
"apl-run \"(⍳5)[3] × 7\" → 21"
|
||||
(mkrv (apl-run "(⍳5)[3] × 7"))
|
||||
(list 21))
|
||||
|
||||
(apl-test "decimal: 3.7 → 3.7" (mkrv (apl-run "3.7")) (list 3.7))
|
||||
|
||||
(apl-test "decimal: ¯2.5 → -2.5" (mkrv (apl-run "¯2.5")) (list -2.5))
|
||||
|
||||
(apl-test "decimal: 1.5 + 2.5 → 4" (mkrv (apl-run "1.5 + 2.5")) (list 4))
|
||||
|
||||
(apl-test "decimal: ⌊3.7 → 3" (mkrv (apl-run "⌊ 3.7")) (list 3))
|
||||
|
||||
(apl-test "decimal: ⌈3.7 → 4" (mkrv (apl-run "⌈ 3.7")) (list 4))
|
||||
|
||||
(apl-test
|
||||
"⎕← scalar passthrough"
|
||||
(mkrv (apl-run "⎕← 42"))
|
||||
(list 42))
|
||||
|
||||
(apl-test
|
||||
"⎕← vector passthrough"
|
||||
(mkrv (apl-run "⎕← 1 2 3"))
|
||||
(list 1 2 3))
|
||||
|
||||
(apl-test
|
||||
"string: 'abc' → 3-char vector"
|
||||
(mkrv (apl-run "'abc'"))
|
||||
(list "a" "b" "c"))
|
||||
|
||||
(apl-test "string: 'a' is rank-0 scalar" (mksh (apl-run "'a'")) (list))
|
||||
|
||||
(apl-test "string: 'hello' shape (5)" (mksh (apl-run "'hello'")) (list 5))
|
||||
|
||||
(apl-test
|
||||
"named-fn: f ← {⍺+⍵} ⋄ 3 f 4 → 7"
|
||||
(mkrv (apl-run "f ← {⍺+⍵} ⋄ 3 f 4"))
|
||||
(list 7))
|
||||
|
||||
(apl-test
|
||||
"named-fn monadic: sq ← {⍵×⍵} ⋄ sq 7 → 49"
|
||||
(mkrv (apl-run "sq ← {⍵×⍵} ⋄ sq 7"))
|
||||
(list 49))
|
||||
|
||||
(apl-test
|
||||
"named-fn dyadic: hyp ← {((⍺×⍺)+⍵×⍵)} ⋄ 3 hyp 4 → 25"
|
||||
(mkrv (apl-run "hyp ← {((⍺×⍺)+⍵×⍵)} ⋄ 3 hyp 4"))
|
||||
(list 25))
|
||||
|
||||
(apl-test
|
||||
"named-fn: dbl ← {⍵+⍵} ⋄ dbl ⍳5"
|
||||
(mkrv (apl-run "dbl ← {⍵+⍵} ⋄ dbl ⍳5"))
|
||||
(list 2 4 6 8 10))
|
||||
|
||||
(apl-test
|
||||
"named-fn factorial via ∇ recursion"
|
||||
(mkrv (apl-run "fact ← {0=⍵:1 ⋄ ⍵×∇⍵-1} ⋄ fact 5"))
|
||||
(list 120))
|
||||
|
||||
(apl-test
|
||||
"named-fn used twice in expr: dbl ← {⍵+⍵} ⋄ (dbl 3) + dbl 4"
|
||||
(mkrv (apl-run "dbl ← {⍵+⍵} ⋄ (dbl 3) + dbl 4"))
|
||||
(list 14))
|
||||
|
||||
(apl-test
|
||||
"named-fn with vector arg: neg ← {-⍵} ⋄ neg 1 2 3"
|
||||
(mkrv (apl-run "neg ← {-⍵} ⋄ neg 1 2 3"))
|
||||
(list -1 -2 -3))
|
||||
|
||||
(apl-test
|
||||
"multi-axis: M[2;2] → center"
|
||||
(mkrv (apl-run "M ← (3 3) ⍴ ⍳9 ⋄ M[2;2]"))
|
||||
(list 5))
|
||||
|
||||
(apl-test
|
||||
"multi-axis: M[1;] → first row"
|
||||
(mkrv (apl-run "M ← (3 3) ⍴ ⍳9 ⋄ M[1;]"))
|
||||
(list 1 2 3))
|
||||
|
||||
(apl-test
|
||||
"multi-axis: M[;2] → second column"
|
||||
(mkrv (apl-run "M ← (3 3) ⍴ ⍳9 ⋄ M[;2]"))
|
||||
(list 2 5 8))
|
||||
|
||||
(apl-test
|
||||
"multi-axis: M[1 2;1 2] → 2x2 block"
|
||||
(mkrv (apl-run "M ← (2 3) ⍴ ⍳6 ⋄ M[1 2;1 2]"))
|
||||
(list 1 2 4 5))
|
||||
|
||||
(apl-test
|
||||
"multi-axis: M[1 2;1 2] shape (2 2)"
|
||||
(mksh (apl-run "M ← (2 3) ⍴ ⍳6 ⋄ M[1 2;1 2]"))
|
||||
(list 2 2))
|
||||
|
||||
(apl-test
|
||||
"multi-axis: M[;] full matrix"
|
||||
(mkrv (apl-run "M ← (2 2) ⍴ 10 20 30 40 ⋄ M[;]"))
|
||||
(list 10 20 30 40))
|
||||
|
||||
(apl-test
|
||||
"multi-axis: M[1;] shape collapsed"
|
||||
(mksh (apl-run "M ← (3 3) ⍴ ⍳9 ⋄ M[1;]"))
|
||||
(list 3))
|
||||
|
||||
(apl-test
|
||||
"multi-axis: select all rows of column 3"
|
||||
(mkrv (apl-run "M ← (4 3) ⍴ 1 2 3 4 5 6 7 8 9 10 11 12 ⋄ M[;3]"))
|
||||
(list 3 6 9 12))
|
||||
|
||||
(apl-test
|
||||
"train: mean = (+/÷≢) on 1..5"
|
||||
(mkrv (apl-run "(+/÷≢) 1 2 3 4 5"))
|
||||
(list 3))
|
||||
|
||||
(apl-test
|
||||
"train: mean of 2 4 6 8 10"
|
||||
(mkrv (apl-run "(+/÷≢) 2 4 6 8 10"))
|
||||
(list 6))
|
||||
|
||||
(apl-test
|
||||
"train 2-atop: (- ⌊) 5 → -5"
|
||||
(mkrv (apl-run "(- ⌊) 5"))
|
||||
(list -5))
|
||||
|
||||
(apl-test
|
||||
"train 3-fork dyadic: 2(+×-)5 → -21"
|
||||
(mkrv (apl-run "2 (+ × -) 5"))
|
||||
(list -21))
|
||||
|
||||
(apl-test
|
||||
"train: range = (⌈/-⌊/) on vector"
|
||||
(mkrv (apl-run "(⌈/-⌊/) 3 1 4 1 5 9 2 6"))
|
||||
(list 8))
|
||||
|
||||
(apl-test
|
||||
"train: mean of ⍳10 has shape ()"
|
||||
(mksh (apl-run "(+/÷≢) ⍳10"))
|
||||
(list))
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
; End-to-end tests of the classic-program archetypes — running APL
|
||||
; source through the full pipeline (tokenize → parse → eval-ast → runtime).
|
||||
;
|
||||
; These mirror the algorithms documented in lib/apl/tests/programs/*.apl
|
||||
; but use forms our pipeline supports today (named functions instead of
|
||||
; the inline ⍵← rebinding idiom; multi-stmt over single one-liners).
|
||||
|
||||
(define mkrv (fn (arr) (get arr :ravel)))
|
||||
(define mksh (fn (arr) (get arr :shape)))
|
||||
|
||||
; ---------- factorial via ∇ recursion (cf. n-queens style) ----------
|
||||
|
||||
(apl-test
|
||||
"e2e: factorial 5! = 120"
|
||||
(mkrv (apl-run "fact ← {0=⍵:1 ⋄ ⍵×∇⍵-1} ⋄ fact 5"))
|
||||
(list 120))
|
||||
|
||||
(apl-test
|
||||
"e2e: factorial 7! = 5040"
|
||||
(mkrv (apl-run "fact ← {0=⍵:1 ⋄ ⍵×∇⍵-1} ⋄ fact 7"))
|
||||
(list 5040))
|
||||
|
||||
(apl-test
|
||||
"e2e: factorial via ×/⍳N (no recursion)"
|
||||
(mkrv (apl-run "fact ← {×/⍳⍵} ⋄ fact 6"))
|
||||
(list 720))
|
||||
|
||||
; ---------- sum / triangular numbers (sum-1..N) ----------
|
||||
|
||||
(apl-test
|
||||
"e2e: triangular(10) = 55"
|
||||
(mkrv (apl-run "tri ← {+/⍳⍵} ⋄ tri 10"))
|
||||
(list 55))
|
||||
|
||||
(apl-test
|
||||
"e2e: triangular(100) = 5050"
|
||||
(mkrv (apl-run "tri ← {+/⍳⍵} ⋄ tri 100"))
|
||||
(list 5050))
|
||||
|
||||
; ---------- sum of squares ----------
|
||||
|
||||
(apl-test
|
||||
"e2e: sum-of-squares 1..5 = 55"
|
||||
(mkrv (apl-run "ss ← {+/⍵×⍵} ⋄ ss ⍳5"))
|
||||
(list 55))
|
||||
|
||||
(apl-test
|
||||
"e2e: sum-of-squares 1..10 = 385"
|
||||
(mkrv (apl-run "ss ← {+/⍵×⍵} ⋄ ss ⍳10"))
|
||||
(list 385))
|
||||
|
||||
; ---------- divisor-counting (prime-sieve building blocks) ----------
|
||||
|
||||
(apl-test
|
||||
"e2e: divisor counts 1..5 via outer mod"
|
||||
(mkrv (apl-run "P ← ⍳ 5 ⋄ +⌿ 0 = P ∘.| P"))
|
||||
(list 1 2 2 3 2))
|
||||
|
||||
(apl-test
|
||||
"e2e: divisor counts 1..10"
|
||||
(mkrv (apl-run "P ← ⍳ 10 ⋄ +⌿ 0 = P ∘.| P"))
|
||||
(list 1 2 2 3 2 4 2 4 3 4))
|
||||
|
||||
(apl-test
|
||||
"e2e: prime-mask 1..10 (count==2)"
|
||||
(mkrv (apl-run "P ← ⍳ 10 ⋄ 2 = +⌿ 0 = P ∘.| P"))
|
||||
(list 0 1 1 0 1 0 1 0 0 0))
|
||||
|
||||
; ---------- monadic primitives chained ----------
|
||||
|
||||
(apl-test
|
||||
"e2e: sum of |abs| = 15"
|
||||
(mkrv (apl-run "+/|¯1 ¯2 ¯3 ¯4 ¯5"))
|
||||
(list 15))
|
||||
|
||||
(apl-test
|
||||
"e2e: max of squares 1..6"
|
||||
(mkrv (apl-run "⌈/(⍳6)×⍳6"))
|
||||
(list 36))
|
||||
|
||||
; ---------- nested named functions ----------
|
||||
|
||||
(apl-test
|
||||
"e2e: compose dbl and sq via two named fns"
|
||||
(mkrv (apl-run "dbl ← {⍵+⍵} ⋄ sq ← {⍵×⍵} ⋄ sq dbl 3"))
|
||||
(list 36))
|
||||
|
||||
(apl-test
|
||||
"e2e: max-of-two as named dyadic fn"
|
||||
(mkrv (apl-run "mx ← {⍺⌈⍵} ⋄ 5 mx 3"))
|
||||
(list 5))
|
||||
|
||||
(apl-test
|
||||
"e2e: sqrt-via-newton 1 step from 1 → 2.5"
|
||||
(mkrv (apl-run "step ← {(⍵+⍺÷⍵)÷2} ⋄ 4 step 1"))
|
||||
(list 2.5))
|
||||
@@ -252,8 +252,6 @@
|
||||
|
||||
(apl-test "queens 7 → 40 solutions" (mkrv (apl-queens 7)) (list 40))
|
||||
|
||||
(apl-test "queens 8 → 92 solutions" (mkrv (apl-queens 8)) (list 92))
|
||||
|
||||
(apl-test "permutations of 3 has 6" (len (apl-permutations 3)) 6)
|
||||
|
||||
(apl-test "permutations of 4 has 24" (len (apl-permutations 4)) 24)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
(list "+" "-" "×" "÷" "*" "⍟" "⌈" "⌊" "|" "!" "?" "○" "~" "<" "≤" "=" "≥" ">" "≠"
|
||||
"≢" "≡" "∊" "∧" "∨" "⍱" "⍲" "," "⍪" "⍴" "⌽" "⊖" "⍉" "↑" "↓" "⊂" "⊃" "⊆"
|
||||
"∪" "∩" "⍳" "⍸" "⌷" "⍋" "⍒" "⊥" "⊤" "⊣" "⊢" "⍎" "⍕"
|
||||
"⍺" "⍵" "∇" "/" "⌿" "\\" "⍀" "¨" "⍨" "∘" "." "⍣" "⍤" "⍥" "@" "¯"))
|
||||
"⍺" "⍵" "∇" "/" "\\" "¨" "⍨" "∘" "." "⍣" "⍤" "⍥" "@" "¯"))
|
||||
|
||||
(define apl-glyph?
|
||||
(fn (ch)
|
||||
@@ -138,22 +138,12 @@
|
||||
(begin
|
||||
(consume! "¯")
|
||||
(let ((digits (read-digits! "")))
|
||||
(if (and (< pos src-len) (= (cur-byte) ".")
|
||||
(< (+ pos 1) src-len) (apl-digit? (nth source (+ pos 1))))
|
||||
(begin (advance!)
|
||||
(let ((frac (read-digits! "")))
|
||||
(tok-push! :num (- 0 (string->number (str digits "." frac))))))
|
||||
(tok-push! :num (- 0 (parse-int digits 0)))))
|
||||
(tok-push! :num (- 0 (parse-int digits 0))))
|
||||
(scan!)))
|
||||
((apl-digit? ch)
|
||||
(begin
|
||||
(let ((digits (read-digits! "")))
|
||||
(if (and (< pos src-len) (= (cur-byte) ".")
|
||||
(< (+ pos 1) src-len) (apl-digit? (nth source (+ pos 1))))
|
||||
(begin (advance!)
|
||||
(let ((frac (read-digits! "")))
|
||||
(tok-push! :num (string->number (str digits "." frac)))))
|
||||
(tok-push! :num (parse-int digits 0))))
|
||||
(tok-push! :num (parse-int digits 0)))
|
||||
(scan!)))
|
||||
((= ch "'")
|
||||
(begin
|
||||
@@ -165,9 +155,7 @@
|
||||
(let ((start pos))
|
||||
(begin
|
||||
(if (cur-sw? "⎕") (consume! "⎕") (advance!))
|
||||
(if (and (< pos src-len) (cur-sw? "←"))
|
||||
(consume! "←")
|
||||
(read-ident-cont!))
|
||||
(read-ident-cont!)
|
||||
(tok-push! :name (slice source start pos))
|
||||
(scan!))))
|
||||
(true
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
((= g "⍋") apl-grade-up)
|
||||
((= g "⍒") apl-grade-down)
|
||||
((= g "⎕FMT") apl-quad-fmt)
|
||||
((= g "⎕←") apl-quad-print)
|
||||
(else (error "no monadic fn for glyph")))))
|
||||
|
||||
(define
|
||||
@@ -98,15 +97,6 @@
|
||||
((tag (first node)))
|
||||
(cond
|
||||
((= tag :num) (apl-scalar (nth node 1)))
|
||||
((= tag :str)
|
||||
(let
|
||||
((s (nth node 1)))
|
||||
(if
|
||||
(= (len s) 1)
|
||||
(apl-scalar s)
|
||||
(make-array
|
||||
(list (len s))
|
||||
(map (fn (i) (slice s i (+ i 1))) (range 0 (len s)))))))
|
||||
((= tag :vec)
|
||||
(let
|
||||
((items (rest node)))
|
||||
@@ -149,16 +139,6 @@
|
||||
(apl-eval-ast rhs env)))))
|
||||
((= tag :program) (apl-eval-stmts (rest node) env))
|
||||
((= tag :dfn) node)
|
||||
((= tag :bracket)
|
||||
(let
|
||||
((arr-expr (nth node 1)) (axis-exprs (rest (rest node))))
|
||||
(let
|
||||
((arr (apl-eval-ast arr-expr env))
|
||||
(axes
|
||||
(map
|
||||
(fn (a) (if (= a :all) nil (apl-eval-ast a env)))
|
||||
axis-exprs)))
|
||||
(apl-bracket-multi axes arr))))
|
||||
(else (error (list "apl-eval-ast: unknown node tag" tag node)))))))
|
||||
|
||||
(define
|
||||
@@ -439,36 +419,6 @@
|
||||
((f (apl-resolve-dyadic inner env)))
|
||||
(fn (arr) (apl-commute f arr))))
|
||||
(else (error "apl-resolve-monadic: unsupported op")))))
|
||||
((= tag :fn-name)
|
||||
(let
|
||||
((nm (nth fn-node 1)))
|
||||
(let
|
||||
((bound (get env nm)))
|
||||
(if
|
||||
(and
|
||||
(list? bound)
|
||||
(> (len bound) 0)
|
||||
(= (first bound) :dfn))
|
||||
(fn (arg) (apl-call-dfn-m bound arg))
|
||||
(error "apl-resolve-monadic: name not bound to dfn")))))
|
||||
((= tag :train)
|
||||
(let
|
||||
((fns (rest fn-node)))
|
||||
(let
|
||||
((n (len fns)))
|
||||
(cond
|
||||
((= n 2)
|
||||
(let
|
||||
((g (apl-resolve-monadic (nth fns 0) env))
|
||||
(h (apl-resolve-monadic (nth fns 1) env)))
|
||||
(fn (arg) (g (h arg)))))
|
||||
((= n 3)
|
||||
(let
|
||||
((f (apl-resolve-monadic (nth fns 0) env))
|
||||
(g (apl-resolve-dyadic (nth fns 1) env))
|
||||
(h (apl-resolve-monadic (nth fns 2) env)))
|
||||
(fn (arg) (g (f arg) (h arg)))))
|
||||
(else (error "monadic train arity not 2 or 3"))))))
|
||||
(else (error "apl-resolve-monadic: unknown fn-node tag"))))))
|
||||
|
||||
(define
|
||||
@@ -492,18 +442,6 @@
|
||||
((f (apl-resolve-dyadic inner env)))
|
||||
(fn (a b) (apl-commute-dyadic f a b))))
|
||||
(else (error "apl-resolve-dyadic: unsupported op")))))
|
||||
((= tag :fn-name)
|
||||
(let
|
||||
((nm (nth fn-node 1)))
|
||||
(let
|
||||
((bound (get env nm)))
|
||||
(if
|
||||
(and
|
||||
(list? bound)
|
||||
(> (len bound) 0)
|
||||
(= (first bound) :dfn))
|
||||
(fn (a b) (apl-call-dfn bound a b))
|
||||
(error "apl-resolve-dyadic: name not bound to dfn")))))
|
||||
((= tag :outer)
|
||||
(let
|
||||
((inner (nth fn-node 2)))
|
||||
@@ -517,24 +455,6 @@
|
||||
((f (apl-resolve-dyadic f-node env))
|
||||
(g (apl-resolve-dyadic g-node env)))
|
||||
(fn (a b) (apl-inner f g a b)))))
|
||||
((= tag :train)
|
||||
(let
|
||||
((fns (rest fn-node)))
|
||||
(let
|
||||
((n (len fns)))
|
||||
(cond
|
||||
((= n 2)
|
||||
(let
|
||||
((g (apl-resolve-monadic (nth fns 0) env))
|
||||
(h (apl-resolve-dyadic (nth fns 1) env)))
|
||||
(fn (a b) (g (h a b)))))
|
||||
((= n 3)
|
||||
(let
|
||||
((f (apl-resolve-dyadic (nth fns 0) env))
|
||||
(g (apl-resolve-dyadic (nth fns 1) env))
|
||||
(h (apl-resolve-dyadic (nth fns 2) env)))
|
||||
(fn (a b) (g (f a b) (h a b)))))
|
||||
(else (error "dyadic train arity not 2 or 3"))))))
|
||||
(else (error "apl-resolve-dyadic: unknown fn-node tag"))))))
|
||||
|
||||
(define apl-run (fn (src) (apl-eval-ast (parse-apl src) {})))
|
||||
|
||||
180
lib/guest/hm.sx
180
lib/guest/hm.sx
@@ -1,180 +0,0 @@
|
||||
;; lib/guest/hm.sx — Hindley-Milner type-inference foundations.
|
||||
;;
|
||||
;; Builds on lib/guest/match.sx (terms + unify) and ast.sx (canonical
|
||||
;; AST shapes). This file ships the ALGEBRA — types, schemes, free
|
||||
;; type-vars, generalize / instantiate, substitution composition — so a
|
||||
;; full Algorithm W (or J) can be assembled on top either inside this
|
||||
;; file or in a host-specific consumer (haskell/infer.sx,
|
||||
;; lib/ocaml/types.sx, …).
|
||||
;;
|
||||
;; Per the brief the second consumer for this step is OCaml-on-SX
|
||||
;; Phase 5 (paired sequencing). Until that lands, the algebra is the
|
||||
;; deliverable; the host-flavoured assembly (lambda / app / let
|
||||
;; inference rules with substitution threading) lives in the host.
|
||||
;;
|
||||
;; Types
|
||||
;; -----
|
||||
;; A type is a canonical match.sx term — type variables use mk-var,
|
||||
;; type constructors use mk-ctor:
|
||||
;; (hm-tv NAME) type variable
|
||||
;; (hm-arrow A B) A -> B
|
||||
;; (hm-con NAME ARGS) named n-ary constructor
|
||||
;; (hm-int) / (hm-bool) / (hm-string) primitive constructors
|
||||
;;
|
||||
;; Schemes
|
||||
;; -------
|
||||
;; (hm-scheme VARS TYPE) ∀ VARS . TYPE
|
||||
;; (hm-monotype TYPE) empty quantifier
|
||||
;; (hm-scheme? S) (hm-scheme-vars S) (hm-scheme-type S)
|
||||
;;
|
||||
;; Free type variables
|
||||
;; -------------------
|
||||
;; (hm-ftv TYPE) names occurring in TYPE
|
||||
;; (hm-ftv-scheme S) free names (minus quantifiers)
|
||||
;; (hm-ftv-env ENV) free across an env (name -> scheme)
|
||||
;;
|
||||
;; Substitution
|
||||
;; ------------
|
||||
;; (hm-apply SUBST TYPE) substitute through a type
|
||||
;; (hm-apply-scheme SUBST S) leaves bound vars alone
|
||||
;; (hm-apply-env SUBST ENV)
|
||||
;; (hm-compose S2 S1) apply S1 then S2
|
||||
;;
|
||||
;; Generalize / Instantiate
|
||||
;; ------------------------
|
||||
;; (hm-generalize TYPE ENV) → scheme over ftv(t) - ftv(env)
|
||||
;; (hm-instantiate SCHEME COUNTER) → fresh-var instance
|
||||
;; (hm-fresh-tv COUNTER) → (:var "tN"), bumps COUNTER
|
||||
;;
|
||||
;; Inference (literal only — the rest of Algorithm W lives in the host)
|
||||
;; --------------------------------------------------------------------
|
||||
;; (hm-infer-literal EXPR) → {:subst {} :type T}
|
||||
;;
|
||||
;; A complete Algorithm W consumes this kit by assembling lambda / app
|
||||
;; / let rules in the host language file.
|
||||
|
||||
(define hm-tv (fn (name) (list :var name)))
|
||||
(define hm-con (fn (name args) (list :ctor name args)))
|
||||
(define hm-arrow (fn (a b) (hm-con "->" (list a b))))
|
||||
(define hm-int (fn () (hm-con "Int" (list))))
|
||||
(define hm-bool (fn () (hm-con "Bool" (list))))
|
||||
(define hm-string (fn () (hm-con "String" (list))))
|
||||
|
||||
(define hm-scheme (fn (vars t) (list :scheme vars t)))
|
||||
(define hm-monotype (fn (t) (hm-scheme (list) t)))
|
||||
(define hm-scheme? (fn (s) (and (list? s) (not (empty? s)) (= (first s) :scheme))))
|
||||
(define hm-scheme-vars (fn (s) (nth s 1)))
|
||||
(define hm-scheme-type (fn (s) (nth s 2)))
|
||||
|
||||
(define
|
||||
hm-fresh-tv
|
||||
(fn (counter)
|
||||
(let ((n (first counter)))
|
||||
(begin
|
||||
(set-nth! counter 0 (+ n 1))
|
||||
(hm-tv (str "t" (+ n 1)))))))
|
||||
|
||||
(define
|
||||
hm-ftv-acc
|
||||
(fn (t acc)
|
||||
(cond
|
||||
((is-var? t)
|
||||
(if (some (fn (n) (= n (var-name t))) acc) acc (cons (var-name t) acc)))
|
||||
((is-ctor? t)
|
||||
(let ((a acc))
|
||||
(begin
|
||||
(for-each (fn (x) (set! a (hm-ftv-acc x a))) (ctor-args t))
|
||||
a)))
|
||||
(:else acc))))
|
||||
|
||||
(define hm-ftv (fn (t) (hm-ftv-acc t (list))))
|
||||
|
||||
(define
|
||||
hm-ftv-scheme
|
||||
(fn (s)
|
||||
(let ((qs (hm-scheme-vars s))
|
||||
(all (hm-ftv (hm-scheme-type s))))
|
||||
(filter (fn (n) (not (some (fn (q) (= q n)) qs))) all))))
|
||||
|
||||
(define
|
||||
hm-ftv-env
|
||||
(fn (env)
|
||||
(let ((acc (list)))
|
||||
(begin
|
||||
(for-each
|
||||
(fn (k)
|
||||
(for-each
|
||||
(fn (n)
|
||||
(when (not (some (fn (m) (= m n)) acc))
|
||||
(set! acc (cons n acc))))
|
||||
(hm-ftv-scheme (get env k))))
|
||||
(keys env))
|
||||
acc))))
|
||||
|
||||
(define hm-apply (fn (subst t) (walk* t subst)))
|
||||
|
||||
(define
|
||||
hm-apply-scheme
|
||||
(fn (subst s)
|
||||
(let ((qs (hm-scheme-vars s))
|
||||
(d {}))
|
||||
(begin
|
||||
(for-each
|
||||
(fn (k)
|
||||
(when (not (some (fn (q) (= q k)) qs))
|
||||
(dict-set! d k (get subst k))))
|
||||
(keys subst))
|
||||
(hm-scheme qs (walk* (hm-scheme-type s) d))))))
|
||||
|
||||
(define
|
||||
hm-apply-env
|
||||
(fn (subst env)
|
||||
(let ((d {}))
|
||||
(begin
|
||||
(for-each
|
||||
(fn (k) (dict-set! d k (hm-apply-scheme subst (get env k))))
|
||||
(keys env))
|
||||
d))))
|
||||
|
||||
(define
|
||||
hm-compose
|
||||
(fn (s2 s1)
|
||||
(let ((d {}))
|
||||
(begin
|
||||
(for-each (fn (k) (dict-set! d k (walk* (get s1 k) s2))) (keys s1))
|
||||
(for-each
|
||||
(fn (k) (when (not (has-key? d k)) (dict-set! d k (get s2 k))))
|
||||
(keys s2))
|
||||
d))))
|
||||
|
||||
(define
|
||||
hm-generalize
|
||||
(fn (t env)
|
||||
(let ((tvars (hm-ftv t))
|
||||
(evars (hm-ftv-env env)))
|
||||
(let ((qs (filter (fn (n) (not (some (fn (m) (= m n)) evars))) tvars)))
|
||||
(hm-scheme qs t)))))
|
||||
|
||||
(define
|
||||
hm-instantiate
|
||||
(fn (s counter)
|
||||
(let ((qs (hm-scheme-vars s))
|
||||
(subst {}))
|
||||
(begin
|
||||
(for-each
|
||||
(fn (q) (set! subst (assoc subst q (hm-fresh-tv counter))))
|
||||
qs)
|
||||
(walk* (hm-scheme-type s) subst)))))
|
||||
|
||||
;; Literal inference — the only AST kind whose typing rule is closed
|
||||
;; in the kit. Lambda / app / let live in host code so the host's own
|
||||
;; AST conventions stay untouched.
|
||||
(define
|
||||
hm-infer-literal
|
||||
(fn (expr)
|
||||
(let ((v (ast-literal-value expr)))
|
||||
(cond
|
||||
((number? v) {:subst {} :type (hm-int)})
|
||||
((string? v) {:subst {} :type (hm-string)})
|
||||
((boolean? v) {:subst {} :type (hm-bool)})
|
||||
(:else (error (str "hm-infer-literal: unknown kind: " v)))))))
|
||||
@@ -1,145 +0,0 @@
|
||||
;; lib/guest/layout.sx — configurable off-side / layout-sensitive lexer.
|
||||
;;
|
||||
;; Inserts virtual open / close / separator tokens based on indentation.
|
||||
;; Configurable enough to encode either the Haskell 98 layout rule (let /
|
||||
;; where / do / of opens a virtual brace at the next token's column) or
|
||||
;; a Python-ish indent / dedent rule (a colon at the end of a line opens
|
||||
;; a block at the next non-blank line's column).
|
||||
;;
|
||||
;; Token shape (input + output)
|
||||
;; ----------------------------
|
||||
;; Each token is a dict {:type :value :line :col …}. The kit reads
|
||||
;; only :type / :value / :line / :col and passes everything else
|
||||
;; through. The input stream MUST be free of newline filler tokens
|
||||
;; (preprocess them away with your tokenizer) — line breaks are detected
|
||||
;; by comparing :line of consecutive tokens.
|
||||
;;
|
||||
;; Config
|
||||
;; ------
|
||||
;; :open-keywords list of strings; a token whose :value matches
|
||||
;; opens a new layout block at the next token's
|
||||
;; column (Haskell: let/where/do/of).
|
||||
;; :open-trailing-fn (fn (tok) -> bool) — alternative trigger that
|
||||
;; fires AFTER the token is emitted. Use for
|
||||
;; Python-style trailing `:`.
|
||||
;; :open-token / :close-token / :sep-token
|
||||
;; templates {:type :value} merged with :line and
|
||||
;; :col when virtual tokens are emitted.
|
||||
;; :explicit-open? (fn (tok) -> bool) — if the next token after a
|
||||
;; trigger satisfies this, suppress virtual layout
|
||||
;; for that block (Haskell: `{`).
|
||||
;; :module-prelude? if true, wrap whole input in an implicit block
|
||||
;; at the first token's column (Haskell yes,
|
||||
;; Python no).
|
||||
;;
|
||||
;; Public entry
|
||||
;; ------------
|
||||
;; (layout-pass cfg tokens) -> tokens with virtual layout inserted.
|
||||
|
||||
(define
|
||||
layout-mk-virtual
|
||||
(fn (template line col)
|
||||
(assoc (assoc template :line line) :col col)))
|
||||
|
||||
(define
|
||||
layout-is-open-kw?
|
||||
(fn (tok open-kws)
|
||||
(and (= (get tok :type) "reserved")
|
||||
(some (fn (k) (= k (get tok :value))) open-kws))))
|
||||
|
||||
(define
|
||||
layout-pass
|
||||
(fn (cfg tokens)
|
||||
(let ((open-kws (get cfg :open-keywords))
|
||||
(trailing-fn (get cfg :open-trailing-fn))
|
||||
(open-tmpl (get cfg :open-token))
|
||||
(close-tmpl (get cfg :close-token))
|
||||
(sep-tmpl (get cfg :sep-token))
|
||||
(mod-prelude? (get cfg :module-prelude?))
|
||||
(expl?-fn (get cfg :explicit-open?))
|
||||
(out (list))
|
||||
(stack (list))
|
||||
(n (len tokens))
|
||||
(i 0)
|
||||
(prev-line -1)
|
||||
(pending-open false)
|
||||
(just-opened false))
|
||||
(define
|
||||
emit-closes-while-greater
|
||||
(fn (col line)
|
||||
(when (and (not (empty? stack)) (> (first stack) col))
|
||||
(do
|
||||
(append! out (layout-mk-virtual close-tmpl line col))
|
||||
(set! stack (rest stack))
|
||||
(emit-closes-while-greater col line)))))
|
||||
(define
|
||||
emit-pending-open
|
||||
(fn (line col)
|
||||
(do
|
||||
(append! out (layout-mk-virtual open-tmpl line col))
|
||||
(set! stack (cons col stack))
|
||||
(set! pending-open false)
|
||||
(set! just-opened true))))
|
||||
(define
|
||||
layout-step
|
||||
(fn ()
|
||||
(when (< i n)
|
||||
(let ((tok (nth tokens i)))
|
||||
(let ((line (get tok :line)) (col (get tok :col)))
|
||||
(cond
|
||||
(pending-open
|
||||
(cond
|
||||
((and (not (= expl?-fn nil)) (expl?-fn tok))
|
||||
(do
|
||||
(set! pending-open false)
|
||||
(append! out tok)
|
||||
(set! prev-line line)
|
||||
(set! i (+ i 1))
|
||||
(layout-step)))
|
||||
(:else
|
||||
(do
|
||||
(emit-pending-open line col)
|
||||
(layout-step)))))
|
||||
(:else
|
||||
(let ((on-fresh-line? (and (> prev-line 0) (> line prev-line))))
|
||||
(do
|
||||
(when on-fresh-line?
|
||||
(let ((stack-before stack))
|
||||
(begin
|
||||
(emit-closes-while-greater col line)
|
||||
(when (and (not (empty? stack))
|
||||
(= (first stack) col)
|
||||
(not just-opened)
|
||||
;; suppress separator if a dedent fired
|
||||
;; — the dedent is itself the separator
|
||||
(= (len stack) (len stack-before)))
|
||||
(append! out (layout-mk-virtual sep-tmpl line col))))))
|
||||
(set! just-opened false)
|
||||
(append! out tok)
|
||||
(set! prev-line line)
|
||||
(set! i (+ i 1))
|
||||
(cond
|
||||
((layout-is-open-kw? tok open-kws)
|
||||
(set! pending-open true))
|
||||
((and (not (= trailing-fn nil)) (trailing-fn tok))
|
||||
(set! pending-open true)))
|
||||
(layout-step))))))))))
|
||||
(begin
|
||||
;; Module prelude: implicit layout block at the first token's column.
|
||||
(when (and mod-prelude? (> n 0))
|
||||
(let ((tok (nth tokens 0)))
|
||||
(do
|
||||
(append! out (layout-mk-virtual open-tmpl (get tok :line) (get tok :col)))
|
||||
(set! stack (cons (get tok :col) stack))
|
||||
(set! just-opened true))))
|
||||
(layout-step)
|
||||
;; EOF: close every remaining block.
|
||||
(define close-rest
|
||||
(fn ()
|
||||
(when (not (empty? stack))
|
||||
(do
|
||||
(append! out (layout-mk-virtual close-tmpl 0 0))
|
||||
(set! stack (rest stack))
|
||||
(close-rest)))))
|
||||
(close-rest)
|
||||
out))))
|
||||
@@ -1,89 +0,0 @@
|
||||
;; lib/guest/tests/hm.sx — exercises lib/guest/hm.sx algebra.
|
||||
|
||||
(define ghm-test-pass 0)
|
||||
(define ghm-test-fail 0)
|
||||
(define ghm-test-fails (list))
|
||||
|
||||
(define
|
||||
ghm-test
|
||||
(fn (name actual expected)
|
||||
(if (= actual expected)
|
||||
(set! ghm-test-pass (+ ghm-test-pass 1))
|
||||
(begin
|
||||
(set! ghm-test-fail (+ ghm-test-fail 1))
|
||||
(append! ghm-test-fails {:name name :expected expected :actual actual})))))
|
||||
|
||||
;; ── Type constructors ─────────────────────────────────────────────
|
||||
(ghm-test "tv" (hm-tv "a") (list :var "a"))
|
||||
(ghm-test "int" (hm-int) (list :ctor "Int" (list)))
|
||||
(ghm-test "arrow" (ctor-head (hm-arrow (hm-int) (hm-bool))) "->")
|
||||
(ghm-test "arrow-args-len" (len (ctor-args (hm-arrow (hm-int) (hm-bool)))) 2)
|
||||
|
||||
;; ── Schemes ───────────────────────────────────────────────────────
|
||||
(ghm-test "scheme-vars" (hm-scheme-vars (hm-scheme (list "a") (hm-tv "a"))) (list "a"))
|
||||
(ghm-test "monotype-vars" (hm-scheme-vars (hm-monotype (hm-int))) (list))
|
||||
(ghm-test "scheme?-yes" (hm-scheme? (hm-monotype (hm-int))) true)
|
||||
(ghm-test "scheme?-no" (hm-scheme? (hm-int)) false)
|
||||
|
||||
;; ── Fresh tyvars ──────────────────────────────────────────────────
|
||||
(ghm-test "fresh-1"
|
||||
(let ((c (list 0))) (var-name (hm-fresh-tv c))) "t1")
|
||||
(ghm-test "fresh-bumps"
|
||||
(let ((c (list 5))) (begin (hm-fresh-tv c) (first c))) 6)
|
||||
|
||||
;; ── Free type variables ──────────────────────────────────────────
|
||||
(ghm-test "ftv-int" (hm-ftv (hm-int)) (list))
|
||||
(ghm-test "ftv-tv" (hm-ftv (hm-tv "a")) (list "a"))
|
||||
(ghm-test "ftv-arrow"
|
||||
(len (hm-ftv (hm-arrow (hm-tv "a") (hm-arrow (hm-tv "b") (hm-tv "a"))))) 2)
|
||||
(ghm-test "ftv-scheme-quantified"
|
||||
(hm-ftv-scheme (hm-scheme (list "a") (hm-arrow (hm-tv "a") (hm-tv "b")))) (list "b"))
|
||||
(ghm-test "ftv-env"
|
||||
(let ((env (assoc {} "f" (hm-monotype (hm-arrow (hm-tv "x") (hm-tv "y"))))))
|
||||
(len (hm-ftv-env env))) 2)
|
||||
|
||||
;; ── Substitution / apply / compose ───────────────────────────────
|
||||
(ghm-test "apply-tv"
|
||||
(hm-apply (assoc {} "a" (hm-int)) (hm-tv "a")) (hm-int))
|
||||
(ghm-test "apply-arrow"
|
||||
(ctor-head
|
||||
(hm-apply (assoc {} "a" (hm-int))
|
||||
(hm-arrow (hm-tv "a") (hm-tv "b")))) "->")
|
||||
(ghm-test "compose-1-then-2"
|
||||
(var-name
|
||||
(hm-apply
|
||||
(hm-compose (assoc {} "b" (hm-tv "c")) (assoc {} "a" (hm-tv "b")))
|
||||
(hm-tv "a"))) "c")
|
||||
|
||||
;; ── Generalize / Instantiate ─────────────────────────────────────
|
||||
;; forall a. a -> a instantiated twice yields fresh vars each time
|
||||
(ghm-test "generalize-id"
|
||||
(len (hm-scheme-vars (hm-generalize (hm-arrow (hm-tv "a") (hm-tv "a")) {}))) 1)
|
||||
|
||||
(ghm-test "generalize-skips-env"
|
||||
;; ftv(t)={a,b}, ftv(env)={a}, qs={b}
|
||||
(let ((env (assoc {} "x" (hm-monotype (hm-tv "a")))))
|
||||
(len (hm-scheme-vars
|
||||
(hm-generalize (hm-arrow (hm-tv "a") (hm-tv "b")) env)))) 1)
|
||||
|
||||
(ghm-test "instantiate-fresh"
|
||||
(let ((s (hm-scheme (list "a") (hm-arrow (hm-tv "a") (hm-tv "a"))))
|
||||
(c (list 0)))
|
||||
(let ((t1 (hm-instantiate s c)) (t2 (hm-instantiate s c)))
|
||||
(not (= (var-name (first (ctor-args t1)))
|
||||
(var-name (first (ctor-args t2)))))))
|
||||
true)
|
||||
|
||||
;; ── Inference (literal only) ─────────────────────────────────────
|
||||
(ghm-test "infer-int"
|
||||
(ctor-head (get (hm-infer-literal (ast-literal 42)) :type)) "Int")
|
||||
(ghm-test "infer-string"
|
||||
(ctor-head (get (hm-infer-literal (ast-literal "hi")) :type)) "String")
|
||||
(ghm-test "infer-bool"
|
||||
(ctor-head (get (hm-infer-literal (ast-literal true)) :type)) "Bool")
|
||||
|
||||
(define ghm-tests-run!
|
||||
(fn ()
|
||||
{:passed ghm-test-pass
|
||||
:failed ghm-test-fail
|
||||
:total (+ ghm-test-pass ghm-test-fail)}))
|
||||
@@ -1,180 +0,0 @@
|
||||
;; lib/guest/tests/layout.sx — synthetic Python-ish off-side fixture.
|
||||
;;
|
||||
;; Exercises lib/guest/layout.sx with a config different from Haskell's
|
||||
;; (no module-prelude, layout opens via trailing `:` not via reserved
|
||||
;; keyword) to prove the kit isn't Haskell-shaped.
|
||||
|
||||
(define glayout-test-pass 0)
|
||||
(define glayout-test-fail 0)
|
||||
(define glayout-test-fails (list))
|
||||
|
||||
(define
|
||||
glayout-test
|
||||
(fn (name actual expected)
|
||||
(if (= actual expected)
|
||||
(set! glayout-test-pass (+ glayout-test-pass 1))
|
||||
(begin
|
||||
(set! glayout-test-fail (+ glayout-test-fail 1))
|
||||
(append! glayout-test-fails {:name name :expected expected :actual actual})))))
|
||||
|
||||
;; Convenience: build a token from {type value line col}.
|
||||
(define
|
||||
glayout-tok
|
||||
(fn (ty val line col)
|
||||
{:type ty :value val :line line :col col}))
|
||||
|
||||
;; Project a token list to ((type value) ...) for compact comparison.
|
||||
(define
|
||||
glayout-shape
|
||||
(fn (toks)
|
||||
(map (fn (t) (list (get t :type) (get t :value))) toks)))
|
||||
|
||||
;; ── Haskell-flavour: keyword opens block ─────────────────────────
|
||||
(define
|
||||
glayout-haskell-cfg
|
||||
{:open-keywords (list "let" "where" "do" "of")
|
||||
:open-trailing-fn nil
|
||||
:open-token {:type "vlbrace" :value "{"}
|
||||
:close-token {:type "vrbrace" :value "}"}
|
||||
:sep-token {:type "vsemi" :value ";"}
|
||||
:module-prelude? false
|
||||
:explicit-open? (fn (tok) (= (get tok :type) "lbrace"))})
|
||||
|
||||
;; do
|
||||
;; a
|
||||
;; b
|
||||
;; c ← outside the do-block
|
||||
(glayout-test "haskell-do-block"
|
||||
(glayout-shape
|
||||
(layout-pass
|
||||
glayout-haskell-cfg
|
||||
(list (glayout-tok "reserved" "do" 1 1)
|
||||
(glayout-tok "ident" "a" 2 3)
|
||||
(glayout-tok "ident" "b" 3 3)
|
||||
(glayout-tok "ident" "c" 4 1))))
|
||||
(list (list "reserved" "do")
|
||||
(list "vlbrace" "{")
|
||||
(list "ident" "a")
|
||||
(list "vsemi" ";")
|
||||
(list "ident" "b")
|
||||
(list "vrbrace" "}")
|
||||
(list "ident" "c")))
|
||||
|
||||
;; Explicit `{` after `do` suppresses virtual layout.
|
||||
(glayout-test "haskell-explicit-brace"
|
||||
(glayout-shape
|
||||
(layout-pass
|
||||
glayout-haskell-cfg
|
||||
(list (glayout-tok "reserved" "do" 1 1)
|
||||
(glayout-tok "lbrace" "{" 1 4)
|
||||
(glayout-tok "ident" "a" 1 6)
|
||||
(glayout-tok "rbrace" "}" 1 8))))
|
||||
(list (list "reserved" "do")
|
||||
(list "lbrace" "{")
|
||||
(list "ident" "a")
|
||||
(list "rbrace" "}")))
|
||||
|
||||
;; Single-statement do-block on the same line.
|
||||
(glayout-test "haskell-do-inline"
|
||||
(glayout-shape
|
||||
(layout-pass
|
||||
glayout-haskell-cfg
|
||||
(list (glayout-tok "reserved" "do" 1 1)
|
||||
(glayout-tok "ident" "a" 1 4))))
|
||||
(list (list "reserved" "do")
|
||||
(list "vlbrace" "{")
|
||||
(list "ident" "a")
|
||||
(list "vrbrace" "}")))
|
||||
|
||||
;; Module-prelude: wrap whole input in implicit layout block at first
|
||||
;; tok's column.
|
||||
(glayout-test "haskell-module-prelude"
|
||||
(glayout-shape
|
||||
(layout-pass
|
||||
(assoc glayout-haskell-cfg :module-prelude? true)
|
||||
(list (glayout-tok "ident" "x" 1 1)
|
||||
(glayout-tok "ident" "y" 2 1)
|
||||
(glayout-tok "ident" "z" 3 1))))
|
||||
(list (list "vlbrace" "{")
|
||||
(list "ident" "x")
|
||||
(list "vsemi" ";")
|
||||
(list "ident" "y")
|
||||
(list "vsemi" ";")
|
||||
(list "ident" "z")
|
||||
(list "vrbrace" "}")))
|
||||
|
||||
;; ── Python-flavour: trailing `:` opens block ─────────────────────
|
||||
(define
|
||||
glayout-python-cfg
|
||||
{:open-keywords (list)
|
||||
:open-trailing-fn (fn (tok) (and (= (get tok :type) "punct")
|
||||
(= (get tok :value) ":")))
|
||||
:open-token {:type "indent" :value "INDENT"}
|
||||
:close-token {:type "dedent" :value "DEDENT"}
|
||||
:sep-token {:type "newline" :value "NEWLINE"}
|
||||
:module-prelude? false
|
||||
:explicit-open? nil})
|
||||
|
||||
;; if x:
|
||||
;; a
|
||||
;; b
|
||||
;; c
|
||||
(glayout-test "python-if-block"
|
||||
(glayout-shape
|
||||
(layout-pass
|
||||
glayout-python-cfg
|
||||
(list (glayout-tok "reserved" "if" 1 1)
|
||||
(glayout-tok "ident" "x" 1 4)
|
||||
(glayout-tok "punct" ":" 1 5)
|
||||
(glayout-tok "ident" "a" 2 5)
|
||||
(glayout-tok "ident" "b" 3 5)
|
||||
(glayout-tok "ident" "c" 4 1))))
|
||||
(list (list "reserved" "if")
|
||||
(list "ident" "x")
|
||||
(list "punct" ":")
|
||||
(list "indent" "INDENT")
|
||||
(list "ident" "a")
|
||||
(list "newline" "NEWLINE")
|
||||
(list "ident" "b")
|
||||
(list "dedent" "DEDENT")
|
||||
(list "ident" "c")))
|
||||
|
||||
;; Nested Python-style blocks.
|
||||
;; def f():
|
||||
;; if x:
|
||||
;; a
|
||||
;; b
|
||||
(glayout-test "python-nested"
|
||||
(glayout-shape
|
||||
(layout-pass
|
||||
glayout-python-cfg
|
||||
(list (glayout-tok "reserved" "def" 1 1)
|
||||
(glayout-tok "ident" "f" 1 5)
|
||||
(glayout-tok "punct" "(" 1 6)
|
||||
(glayout-tok "punct" ")" 1 7)
|
||||
(glayout-tok "punct" ":" 1 8)
|
||||
(glayout-tok "reserved" "if" 2 5)
|
||||
(glayout-tok "ident" "x" 2 8)
|
||||
(glayout-tok "punct" ":" 2 9)
|
||||
(glayout-tok "ident" "a" 3 9)
|
||||
(glayout-tok "ident" "b" 4 5))))
|
||||
(list (list "reserved" "def")
|
||||
(list "ident" "f")
|
||||
(list "punct" "(")
|
||||
(list "punct" ")")
|
||||
(list "punct" ":")
|
||||
(list "indent" "INDENT")
|
||||
(list "reserved" "if")
|
||||
(list "ident" "x")
|
||||
(list "punct" ":")
|
||||
(list "indent" "INDENT")
|
||||
(list "ident" "a")
|
||||
(list "dedent" "DEDENT")
|
||||
(list "ident" "b")
|
||||
(list "dedent" "DEDENT")))
|
||||
|
||||
(define glayout-tests-run!
|
||||
(fn ()
|
||||
{:passed glayout-test-pass
|
||||
:failed glayout-test-fail
|
||||
:total (+ glayout-test-pass glayout-test-fail)}))
|
||||
293
lib/minikanren/tests/unify.sx
Normal file
293
lib/minikanren/tests/unify.sx
Normal file
@@ -0,0 +1,293 @@
|
||||
;; lib/minikanren/tests/unify.sx — Phase 1 tests for unify.sx.
|
||||
;;
|
||||
;; Loads into a session that already has lib/guest/match.sx and
|
||||
;; lib/minikanren/unify.sx defined. Tests are top-level forms.
|
||||
;; Call (mk-tests-run!) afterwards to get the totals.
|
||||
;;
|
||||
;; Note: SX dict equality is reference-based, so tests check the *effect*
|
||||
;; of a unification (success/failure flag, or walked bindings) rather than
|
||||
;; the raw substitution dict.
|
||||
|
||||
(define mk-test-pass 0)
|
||||
(define mk-test-fail 0)
|
||||
(define mk-test-fails (list))
|
||||
|
||||
(define
|
||||
mk-test
|
||||
(fn
|
||||
(name actual expected)
|
||||
(if
|
||||
(= actual expected)
|
||||
(set! mk-test-pass (+ mk-test-pass 1))
|
||||
(begin
|
||||
(set! mk-test-fail (+ mk-test-fail 1))
|
||||
(append! mk-test-fails {:name name :expected expected :actual actual})))))
|
||||
|
||||
(define mk-tests-run! (fn () {:total (+ mk-test-pass mk-test-fail) :passed mk-test-pass :failed mk-test-fail :fails mk-test-fails}))
|
||||
|
||||
(define mk-unified? (fn (s) (if (= s nil) false true)))
|
||||
|
||||
;; --- fresh variable construction ---
|
||||
|
||||
(mk-test
|
||||
"make-var-distinct"
|
||||
(let ((a (make-var)) (b (make-var))) (= (var-name a) (var-name b)))
|
||||
false)
|
||||
|
||||
(mk-test "make-var-is-var" (mk-var? (make-var)) true)
|
||||
(mk-test "var?-num" (mk-var? 5) false)
|
||||
(mk-test "var?-list" (mk-var? (list 1 2)) false)
|
||||
(mk-test "var?-string" (mk-var? "hi") false)
|
||||
(mk-test "var?-empty" (mk-var? (list)) false)
|
||||
(mk-test "var?-bool" (mk-var? true) false)
|
||||
|
||||
;; --- empty substitution ---
|
||||
|
||||
(mk-test "empty-s-walk-num" (mk-walk 5 empty-s) 5)
|
||||
(mk-test "empty-s-walk-str" (mk-walk "x" empty-s) "x")
|
||||
(mk-test
|
||||
"empty-s-walk-list"
|
||||
(mk-walk (list 1 2) empty-s)
|
||||
(list 1 2))
|
||||
(mk-test
|
||||
"empty-s-walk-unbound-var"
|
||||
(let ((x (make-var))) (= (mk-walk x empty-s) x))
|
||||
true)
|
||||
|
||||
;; --- walk: top-level chain resolution ---
|
||||
|
||||
(mk-test
|
||||
"walk-direct-binding"
|
||||
(mk-walk (mk-var "x") (extend "x" 7 empty-s))
|
||||
7)
|
||||
|
||||
(mk-test
|
||||
"walk-two-step-chain"
|
||||
(mk-walk
|
||||
(mk-var "x")
|
||||
(extend "x" (mk-var "y") (extend "y" 9 empty-s)))
|
||||
9)
|
||||
|
||||
(mk-test
|
||||
"walk-three-step-chain"
|
||||
(mk-walk
|
||||
(mk-var "a")
|
||||
(extend
|
||||
"a"
|
||||
(mk-var "b")
|
||||
(extend "b" (mk-var "c") (extend "c" 42 empty-s))))
|
||||
42)
|
||||
|
||||
(mk-test
|
||||
"walk-stops-at-list"
|
||||
(mk-walk (list 1 (mk-var "x")) (extend "x" 5 empty-s))
|
||||
(list 1 (mk-var "x")))
|
||||
|
||||
;; --- walk*: deep walk into lists ---
|
||||
|
||||
(mk-test
|
||||
"walk*-flat-list-with-vars"
|
||||
(mk-walk*
|
||||
(list (mk-var "x") 2 (mk-var "y"))
|
||||
(extend "x" 1 (extend "y" 3 empty-s)))
|
||||
(list 1 2 3))
|
||||
|
||||
(mk-test
|
||||
"walk*-nested-list"
|
||||
(mk-walk*
|
||||
(list 1 (mk-var "x") (list 2 (mk-var "y")))
|
||||
(extend "x" 5 (extend "y" 6 empty-s)))
|
||||
(list 1 5 (list 2 6)))
|
||||
|
||||
(mk-test
|
||||
"walk*-unbound-stays-var"
|
||||
(let
|
||||
((x (mk-var "x")))
|
||||
(= (mk-walk* (list 1 x) empty-s) (list 1 x)))
|
||||
true)
|
||||
|
||||
(mk-test "walk*-atom" (mk-walk* 5 empty-s) 5)
|
||||
|
||||
;; --- unify atoms (success / failure semantics, not dict shape) ---
|
||||
|
||||
(mk-test
|
||||
"unify-num-eq-succeeds"
|
||||
(mk-unified? (mk-unify 5 5 empty-s))
|
||||
true)
|
||||
(mk-test "unify-num-neq-fails" (mk-unify 5 6 empty-s) nil)
|
||||
(mk-test
|
||||
"unify-str-eq-succeeds"
|
||||
(mk-unified? (mk-unify "a" "a" empty-s))
|
||||
true)
|
||||
(mk-test "unify-str-neq-fails" (mk-unify "a" "b" empty-s) nil)
|
||||
(mk-test
|
||||
"unify-bool-eq-succeeds"
|
||||
(mk-unified? (mk-unify true true empty-s))
|
||||
true)
|
||||
(mk-test "unify-bool-neq-fails" (mk-unify true false empty-s) nil)
|
||||
(mk-test
|
||||
"unify-nil-eq-succeeds"
|
||||
(mk-unified? (mk-unify nil nil empty-s))
|
||||
true)
|
||||
(mk-test
|
||||
"unify-empty-list-succeeds"
|
||||
(mk-unified? (mk-unify (list) (list) empty-s))
|
||||
true)
|
||||
|
||||
;; --- unify var with anything (walk to verify binding) ---
|
||||
|
||||
(mk-test
|
||||
"unify-var-num-binds"
|
||||
(mk-walk (mk-var "x") (mk-unify (mk-var "x") 5 empty-s))
|
||||
5)
|
||||
|
||||
(mk-test
|
||||
"unify-num-var-binds"
|
||||
(mk-walk (mk-var "x") (mk-unify 5 (mk-var "x") empty-s))
|
||||
5)
|
||||
|
||||
(mk-test
|
||||
"unify-var-list-binds"
|
||||
(mk-walk
|
||||
(mk-var "x")
|
||||
(mk-unify (mk-var "x") (list 1 2) empty-s))
|
||||
(list 1 2))
|
||||
|
||||
(mk-test
|
||||
"unify-var-var-same-no-extend"
|
||||
(mk-unified? (mk-unify (mk-var "x") (mk-var "x") empty-s))
|
||||
true)
|
||||
|
||||
(mk-test
|
||||
"unify-var-var-different-walks-equal"
|
||||
(let
|
||||
((s (mk-unify (mk-var "x") (mk-var "y") empty-s)))
|
||||
(= (mk-walk (mk-var "x") s) (mk-walk (mk-var "y") s)))
|
||||
true)
|
||||
|
||||
;; --- unify lists positionally ---
|
||||
|
||||
(mk-test
|
||||
"unify-list-equal-succeeds"
|
||||
(mk-unified?
|
||||
(mk-unify
|
||||
(list 1 2 3)
|
||||
(list 1 2 3)
|
||||
empty-s))
|
||||
true)
|
||||
|
||||
(mk-test
|
||||
"unify-list-different-length-fails-1"
|
||||
(mk-unify
|
||||
(list 1 2)
|
||||
(list 1 2 3)
|
||||
empty-s)
|
||||
nil)
|
||||
|
||||
(mk-test
|
||||
"unify-list-different-length-fails-2"
|
||||
(mk-unify
|
||||
(list 1 2 3)
|
||||
(list 1 2)
|
||||
empty-s)
|
||||
nil)
|
||||
|
||||
(mk-test
|
||||
"unify-list-mismatch-fails"
|
||||
(mk-unify
|
||||
(list 1 2)
|
||||
(list 1 3)
|
||||
empty-s)
|
||||
nil)
|
||||
|
||||
(mk-test
|
||||
"unify-list-vs-atom-fails"
|
||||
(mk-unify (list 1 2) 5 empty-s)
|
||||
nil)
|
||||
|
||||
(mk-test
|
||||
"unify-empty-vs-non-empty-fails"
|
||||
(mk-unify (list) (list 1) empty-s)
|
||||
nil)
|
||||
|
||||
(mk-test
|
||||
"unify-list-with-vars-walks"
|
||||
(mk-walk*
|
||||
(list (mk-var "x") (mk-var "y"))
|
||||
(mk-unify
|
||||
(list (mk-var "x") (mk-var "y"))
|
||||
(list 1 2)
|
||||
empty-s))
|
||||
(list 1 2))
|
||||
|
||||
(mk-test
|
||||
"unify-nested-lists-with-vars-walks"
|
||||
(mk-walk*
|
||||
(list (mk-var "x") (list (mk-var "y") 3))
|
||||
(mk-unify
|
||||
(list (mk-var "x") (list (mk-var "y") 3))
|
||||
(list 1 (list 2 3))
|
||||
empty-s))
|
||||
(list 1 (list 2 3)))
|
||||
|
||||
;; --- unify chained substitutions ---
|
||||
|
||||
(mk-test
|
||||
"unify-chain-var-var-then-atom"
|
||||
(let
|
||||
((x (mk-var "x")) (y (mk-var "y")))
|
||||
(let
|
||||
((s1 (mk-unify x y empty-s)))
|
||||
(mk-walk x (mk-unify y 7 s1))))
|
||||
7)
|
||||
|
||||
(mk-test
|
||||
"unify-already-bound-consistent"
|
||||
(let
|
||||
((s (extend "x" 5 empty-s)))
|
||||
(mk-unified? (mk-unify (mk-var "x") 5 s)))
|
||||
true)
|
||||
|
||||
(mk-test
|
||||
"unify-already-bound-conflict-fails"
|
||||
(let
|
||||
((s (extend "x" 5 empty-s)))
|
||||
(mk-unify (mk-var "x") 6 s))
|
||||
nil)
|
||||
|
||||
;; --- occurs check (opt-in) ---
|
||||
|
||||
(mk-test
|
||||
"unify-no-occurs-default-succeeds"
|
||||
(let
|
||||
((x (mk-var "x")))
|
||||
(mk-unified? (mk-unify x (list 1 x) empty-s)))
|
||||
true)
|
||||
|
||||
(mk-test
|
||||
"unify-occurs-direct-fails"
|
||||
(let ((x (mk-var "x"))) (mk-unify-check x (list 1 x) empty-s))
|
||||
nil)
|
||||
|
||||
(mk-test
|
||||
"unify-occurs-nested-fails"
|
||||
(let
|
||||
((x (mk-var "x")))
|
||||
(mk-unify-check x (list 1 (list 2 x)) empty-s))
|
||||
nil)
|
||||
|
||||
(mk-test
|
||||
"unify-occurs-non-occurring-succeeds"
|
||||
(let
|
||||
((x (mk-var "x")))
|
||||
(mk-unified? (mk-unify-check x 5 empty-s)))
|
||||
true)
|
||||
|
||||
(mk-test
|
||||
"unify-occurs-via-chain-fails"
|
||||
(let
|
||||
((x (mk-var "x")) (y (mk-var "y")))
|
||||
(let ((s (extend "y" (list x) empty-s))) (mk-unify-check x y s)))
|
||||
nil)
|
||||
|
||||
(mk-tests-run!)
|
||||
52
lib/minikanren/unify.sx
Normal file
52
lib/minikanren/unify.sx
Normal file
@@ -0,0 +1,52 @@
|
||||
;; lib/minikanren/unify.sx — Phase 1: variables + unification.
|
||||
;;
|
||||
;; miniKanren-on-SX, built on lib/guest/match.sx. The kit ships the heavy
|
||||
;; lifting (walk-with, unify-with, occurs-with, extend, empty-subst,
|
||||
;; mk-var/is-var?/var-name); this file supplies a miniKanren-shaped cfg
|
||||
;; and a thin public API.
|
||||
;;
|
||||
;; Term shape (designed for natural SX use):
|
||||
;; logic var : (:var NAME) — kit's mk-var
|
||||
;; pair : any non-empty SX list — head + tail unified positionally
|
||||
;; atom : number / string / symbol / boolean / nil / ()
|
||||
;; Substitution: SX dict mapping VAR-NAME → term. Empty = (empty-subst).
|
||||
|
||||
(define
|
||||
mk-list-pair?
|
||||
(fn (t) (and (list? t) (not (empty? t)) (not (is-var? t)))))
|
||||
|
||||
(define mk-list-pair-head (fn (t) :pair))
|
||||
(define mk-list-pair-args (fn (t) t))
|
||||
|
||||
(define mk-cfg {:ctor-head mk-list-pair-head :var? is-var? :ctor? mk-list-pair? :occurs-check? false :var-name var-name :ctor-args mk-list-pair-args})
|
||||
|
||||
(define mk-cfg-occurs {:ctor-head mk-list-pair-head :var? is-var? :ctor? mk-list-pair? :occurs-check? true :var-name var-name :ctor-args mk-list-pair-args})
|
||||
|
||||
(define empty-s (empty-subst))
|
||||
|
||||
(define mk-fresh-counter 0)
|
||||
|
||||
(define
|
||||
make-var
|
||||
(fn
|
||||
()
|
||||
(begin
|
||||
(set! mk-fresh-counter (+ mk-fresh-counter 1))
|
||||
(mk-var (str "_." mk-fresh-counter)))))
|
||||
|
||||
(define mk-var? is-var?)
|
||||
|
||||
(define mk-walk (fn (t s) (walk-with mk-cfg t s)))
|
||||
|
||||
(define
|
||||
mk-walk*
|
||||
(fn
|
||||
(t s)
|
||||
(let
|
||||
((w (mk-walk t s)))
|
||||
(cond
|
||||
((mk-list-pair? w) (map (fn (a) (mk-walk* a s)) w))
|
||||
(:else w)))))
|
||||
|
||||
(define mk-unify (fn (u v s) (unify-with mk-cfg u v s)))
|
||||
(define mk-unify-check (fn (u v s) (unify-with mk-cfg-occurs u v s)))
|
||||
@@ -1,23 +0,0 @@
|
||||
let div_sum n =
|
||||
let s = ref 1 in
|
||||
let i = ref 2 in
|
||||
while !i * !i <= n do
|
||||
if n mod !i = 0 then begin
|
||||
s := !s + !i;
|
||||
let q = n / !i in
|
||||
if q <> !i then s := !s + q
|
||||
end;
|
||||
i := !i + 1
|
||||
done;
|
||||
if n = 1 then 0 else !s
|
||||
|
||||
let count_abundant n =
|
||||
let c = ref 0 in
|
||||
for i = 12 to n - 1 do
|
||||
if div_sum i > i then c := !c + 1
|
||||
done;
|
||||
!c
|
||||
|
||||
;;
|
||||
|
||||
count_abundant 100
|
||||
@@ -1,8 +0,0 @@
|
||||
let rec ack m n =
|
||||
if m = 0 then n + 1
|
||||
else if n = 0 then ack (m - 1) 1
|
||||
else ack (m - 1) (ack m (n - 1))
|
||||
|
||||
;;
|
||||
|
||||
ack 3 4
|
||||
@@ -1,31 +0,0 @@
|
||||
let max_nonoverlap intervals =
|
||||
let arr = Array.of_list intervals in
|
||||
let n = Array.length arr in
|
||||
let sorted = Array.make n (0, 0) in
|
||||
for i = 0 to n - 1 do
|
||||
sorted.(i) <- arr.(i)
|
||||
done;
|
||||
for i = 0 to n - 1 do
|
||||
for j = 0 to n - 2 - i do
|
||||
let (_, e1) = sorted.(j) in
|
||||
let (_, e2) = sorted.(j + 1) in
|
||||
if e1 > e2 then begin
|
||||
let t = sorted.(j) in
|
||||
sorted.(j) <- sorted.(j + 1);
|
||||
sorted.(j + 1) <- t
|
||||
end
|
||||
done
|
||||
done;
|
||||
let count = ref 0 in
|
||||
let last_end = ref (-1000000) in
|
||||
for i = 0 to n - 1 do
|
||||
let (s, e) = sorted.(i) in
|
||||
if s >= !last_end then begin
|
||||
count := !count + 1;
|
||||
last_end := e
|
||||
end
|
||||
done;
|
||||
!count
|
||||
;;
|
||||
|
||||
max_nonoverlap [(1, 4); (3, 5); (0, 6); (5, 7); (3, 8); (5, 9); (6, 10); (8, 11); (8, 12); (2, 13); (12, 14)]
|
||||
@@ -1,13 +0,0 @@
|
||||
let adler32 s =
|
||||
let a = ref 1 in
|
||||
let b = ref 0 in
|
||||
let m = 65521 in
|
||||
for i = 0 to String.length s - 1 do
|
||||
a := (!a + Char.code s.[i]) mod m;
|
||||
b := (!b + !a) mod m
|
||||
done;
|
||||
!b * 65536 + !a
|
||||
|
||||
;;
|
||||
|
||||
adler32 "Wikipedia"
|
||||
@@ -1,23 +0,0 @@
|
||||
let to_counts s =
|
||||
let counts = Array.make 256 0 in
|
||||
for i = 0 to String.length s - 1 do
|
||||
let c = Char.code s.[i] in
|
||||
counts.(c) <- counts.(c) + 1
|
||||
done;
|
||||
counts
|
||||
|
||||
let same_counts a b =
|
||||
let result = ref true in
|
||||
for i = 0 to 255 do
|
||||
if a.(i) <> b.(i) then result := false
|
||||
done;
|
||||
!result
|
||||
|
||||
let is_anagram s t = same_counts (to_counts s) (to_counts t)
|
||||
|
||||
;;
|
||||
|
||||
(if is_anagram "listen" "silent" then 1 else 0) +
|
||||
(if is_anagram "hello" "world" then 1 else 0) +
|
||||
(if is_anagram "anagram" "nagaram" then 1 else 0) +
|
||||
(if is_anagram "abc" "abcd" then 1 else 0)
|
||||
@@ -1,29 +0,0 @@
|
||||
let canonical s =
|
||||
let chars = Array.make 26 0 in
|
||||
for i = 0 to String.length s - 1 do
|
||||
let k = Char.code s.[i] - Char.code 'a' in
|
||||
if k >= 0 && k < 26 then chars.(k) <- chars.(k) + 1
|
||||
done;
|
||||
let buf = Buffer.create 26 in
|
||||
for i = 0 to 25 do
|
||||
for _ = 1 to chars.(i) do
|
||||
Buffer.add_string buf (String.make 1 (Char.chr (i + Char.code 'a')))
|
||||
done
|
||||
done;
|
||||
Buffer.contents buf
|
||||
|
||||
let group_anagrams xs =
|
||||
let h = Hashtbl.create 8 in
|
||||
List.iter (fun s ->
|
||||
let k = canonical s in
|
||||
let cur = match Hashtbl.find_opt h k with
|
||||
| Some xs -> xs
|
||||
| None -> []
|
||||
in
|
||||
Hashtbl.replace h k (s :: cur)
|
||||
) xs;
|
||||
Hashtbl.length h
|
||||
|
||||
;;
|
||||
|
||||
group_anagrams ["eat"; "tea"; "tan"; "ate"; "nat"; "bat"]
|
||||
@@ -1,26 +0,0 @@
|
||||
(* Baseline: count anagram groups using Hashtbl + sort *)
|
||||
|
||||
(* Sort the chars in a string to get its anagram-equivalence key *)
|
||||
let canonical s =
|
||||
let n = String.length s in
|
||||
let chars = ref [] in
|
||||
for i = 0 to n - 1 do
|
||||
chars := (String.get s i) :: !chars
|
||||
done ;
|
||||
let sorted = List.sort compare !chars in
|
||||
String.concat "" sorted
|
||||
;;
|
||||
|
||||
let count_groups words =
|
||||
let counts = Hashtbl.create 16 in
|
||||
List.iter
|
||||
(fun w ->
|
||||
let k = canonical w in
|
||||
match Hashtbl.find_opt counts k with
|
||||
| None -> Hashtbl.add counts k 1
|
||||
| Some n -> Hashtbl.replace counts k (n + 1))
|
||||
words ;
|
||||
Hashtbl.length counts
|
||||
;;
|
||||
|
||||
count_groups ["eat"; "tea"; "tan"; "ate"; "nat"; "bat"]
|
||||
@@ -1,17 +0,0 @@
|
||||
type account = { mutable balance : int }
|
||||
|
||||
exception Insufficient
|
||||
|
||||
let withdraw acct amt =
|
||||
if amt > acct.balance then raise Insufficient
|
||||
else acct.balance <- acct.balance - amt
|
||||
|
||||
let deposit acct amt = acct.balance <- acct.balance + amt
|
||||
|
||||
;;
|
||||
|
||||
let a = { balance = 100 } in
|
||||
deposit a 50;
|
||||
withdraw a 30;
|
||||
try (withdraw a 200; -1)
|
||||
with Insufficient -> a.balance
|
||||
@@ -1,18 +0,0 @@
|
||||
let count_words text =
|
||||
let words = String.split_on_char ' ' text in
|
||||
let counts = Hashtbl.create 8 in
|
||||
List.iter (fun w ->
|
||||
let n = match Hashtbl.find_opt counts w with
|
||||
| Some n -> n + 1
|
||||
| None -> 1
|
||||
in
|
||||
Hashtbl.replace counts w n
|
||||
) words;
|
||||
counts
|
||||
|
||||
let max_count counts =
|
||||
Hashtbl.fold (fun _ v acc -> if v > acc then v else acc) counts 0
|
||||
|
||||
;;
|
||||
|
||||
max_count (count_words "the quick brown fox jumps over the lazy dog the fox")
|
||||
@@ -1,25 +0,0 @@
|
||||
let is_balanced s =
|
||||
let stack = Stack.create () in
|
||||
let n = String.length s in
|
||||
let ok = ref true in
|
||||
let i = ref 0 in
|
||||
while !i < n && !ok do
|
||||
let c = s.[!i] in
|
||||
(if c = '(' || c = '[' || c = '{' then Stack.push c stack
|
||||
else if c = ')' then
|
||||
(if Stack.is_empty stack || Stack.pop stack <> '(' then ok := false)
|
||||
else if c = ']' then
|
||||
(if Stack.is_empty stack || Stack.pop stack <> '[' then ok := false)
|
||||
else if c = '}' then
|
||||
(if Stack.is_empty stack || Stack.pop stack <> '{' then ok := false));
|
||||
i := !i + 1
|
||||
done;
|
||||
!ok && Stack.is_empty stack
|
||||
|
||||
;;
|
||||
|
||||
(if is_balanced "({[abc]d}e)" then 1 else 0) +
|
||||
(if is_balanced "(a]" then 1 else 0) +
|
||||
(if is_balanced "{[}]" then 1 else 0) +
|
||||
(if is_balanced "(())" then 1 else 0) +
|
||||
(if is_balanced "" then 1 else 0)
|
||||
@@ -1,19 +0,0 @@
|
||||
let to_base_n n base =
|
||||
let digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" in
|
||||
if n = 0 then "0"
|
||||
else begin
|
||||
let m = ref (abs n) in
|
||||
let acc = ref "" in
|
||||
while !m > 0 do
|
||||
acc := String.make 1 digits.[!m mod base] ^ !acc;
|
||||
m := !m / base
|
||||
done;
|
||||
if n < 0 then "-" ^ !acc else !acc
|
||||
end
|
||||
|
||||
;;
|
||||
|
||||
String.length (to_base_n 255 16) +
|
||||
String.length (to_base_n 1024 2) +
|
||||
String.length (to_base_n 100 10) +
|
||||
String.length (to_base_n 0 16)
|
||||
@@ -1,42 +0,0 @@
|
||||
let interpret prog =
|
||||
let mem = Array.make 256 0 in
|
||||
let ptr = ref 0 in
|
||||
let pc = ref 0 in
|
||||
let n = String.length prog in
|
||||
let acc = ref 0 in
|
||||
while !pc < n do
|
||||
let c = prog.[!pc] in
|
||||
(if c = '>' then ptr := !ptr + 1
|
||||
else if c = '<' then ptr := !ptr - 1
|
||||
else if c = '+' then mem.(!ptr) <- mem.(!ptr) + 1
|
||||
else if c = '-' then mem.(!ptr) <- mem.(!ptr) - 1
|
||||
else if c = '.' then acc := !acc + mem.(!ptr)
|
||||
else if c = '[' then begin
|
||||
if mem.(!ptr) = 0 then begin
|
||||
let depth = ref 1 in
|
||||
while !depth > 0 do
|
||||
pc := !pc + 1;
|
||||
let c = prog.[!pc] in
|
||||
if c = '[' then depth := !depth + 1
|
||||
else if c = ']' then depth := !depth - 1
|
||||
done
|
||||
end
|
||||
end
|
||||
else if c = ']' then begin
|
||||
if mem.(!ptr) <> 0 then begin
|
||||
let depth = ref 1 in
|
||||
while !depth > 0 do
|
||||
pc := !pc - 1;
|
||||
let c = prog.[!pc] in
|
||||
if c = ']' then depth := !depth + 1
|
||||
else if c = '[' then depth := !depth - 1
|
||||
done
|
||||
end
|
||||
end);
|
||||
pc := !pc + 1
|
||||
done;
|
||||
!acc
|
||||
|
||||
;;
|
||||
|
||||
interpret "+++[.-]"
|
||||
@@ -1,43 +0,0 @@
|
||||
(* Baseline: graph BFS using Queue + Hashtbl visited set.
|
||||
Returns the count of reachable nodes. *)
|
||||
|
||||
(* Adjacency as an assoc list of (node, neighbors). *)
|
||||
let graph =
|
||||
[ ("A", ["B"; "C"])
|
||||
; ("B", ["D"])
|
||||
; ("C", ["D"; "E"])
|
||||
; ("D", ["F"])
|
||||
; ("E", ["F"])
|
||||
; ("F", [])
|
||||
]
|
||||
;;
|
||||
|
||||
let neighbors n =
|
||||
match List.assoc_opt n graph with
|
||||
| None -> []
|
||||
| Some ns -> ns
|
||||
;;
|
||||
|
||||
let bfs start =
|
||||
let visited = Hashtbl.create 16 in
|
||||
let q = Queue.create () in
|
||||
Queue.push start q ;
|
||||
Hashtbl.add visited start true ;
|
||||
let rec loop () =
|
||||
if Queue.is_empty q then ()
|
||||
else
|
||||
let v = Queue.pop q in
|
||||
List.iter
|
||||
(fun n ->
|
||||
if not (Hashtbl.mem visited n) then begin
|
||||
Hashtbl.add visited n true ;
|
||||
Queue.push n q
|
||||
end)
|
||||
(neighbors v) ;
|
||||
loop ()
|
||||
in
|
||||
loop () ;
|
||||
Hashtbl.length visited
|
||||
;;
|
||||
|
||||
bfs "A"
|
||||
@@ -1,42 +0,0 @@
|
||||
let h = 5
|
||||
let w = 5
|
||||
|
||||
let grid = [|
|
||||
[| 0; 0; 1; 0; 0 |];
|
||||
[| 1; 0; 1; 0; 1 |];
|
||||
[| 0; 0; 0; 0; 0 |];
|
||||
[| 0; 1; 1; 1; 0 |];
|
||||
[| 0; 0; 0; 0; 0 |]
|
||||
|]
|
||||
|
||||
let step dist q r c nr nc =
|
||||
if nr >= 0 && nr < h && nc >= 0 && nc < w
|
||||
&& grid.(nr).(nc) = 0 && dist.(nr).(nc) = -1 then begin
|
||||
dist.(nr).(nc) <- dist.(r).(c) + 1;
|
||||
Queue.push (nr * 10 + nc) q
|
||||
end
|
||||
|
||||
let bfs sr sc tr tc =
|
||||
let dist = Array.init h (fun _ -> Array.make w (-1)) in
|
||||
let q = Queue.create () in
|
||||
dist.(sr).(sc) <- 0;
|
||||
Queue.push (sr * 10 + sc) q;
|
||||
let go = ref true in
|
||||
while !go do
|
||||
if Queue.is_empty q then go := false
|
||||
else if dist.(tr).(tc) <> -1 then go := false
|
||||
else begin
|
||||
let rc = Queue.pop q in
|
||||
let r = rc / 10 in
|
||||
let c = rc mod 10 in
|
||||
step dist q r c (r - 1) c;
|
||||
step dist q r c (r + 1) c;
|
||||
step dist q r c r (c - 1);
|
||||
step dist q r c r (c + 1)
|
||||
end
|
||||
done;
|
||||
dist.(tr).(tc)
|
||||
|
||||
;;
|
||||
|
||||
bfs 0 0 4 4
|
||||
@@ -1,24 +0,0 @@
|
||||
let bigint_add a b =
|
||||
let rec aux a b carry =
|
||||
match (a, b) with
|
||||
| ([], []) -> if carry = 0 then [] else [carry]
|
||||
| (x :: xs, []) ->
|
||||
let s = x + carry in
|
||||
(s mod 10) :: aux xs [] (s / 10)
|
||||
| ([], y :: ys) ->
|
||||
let s = y + carry in
|
||||
(s mod 10) :: aux [] ys (s / 10)
|
||||
| (x :: xs, y :: ys) ->
|
||||
let s = x + y + carry in
|
||||
(s mod 10) :: aux xs ys (s / 10)
|
||||
in
|
||||
aux a b 0
|
||||
|
||||
;;
|
||||
|
||||
let r1 = bigint_add [9;9;9] [1] in
|
||||
let r2 = bigint_add [5;6;7] [8;9;1] in
|
||||
let r3 = bigint_add [9;9;9;9;9;9;9;9] [1] in
|
||||
List.fold_left (+) 0 r1
|
||||
+ List.fold_left (+) 0 r2
|
||||
+ List.length r3
|
||||
@@ -1,47 +0,0 @@
|
||||
let parent i = (i - 1) / 2
|
||||
let lchild i = 2 * i + 1
|
||||
let rchild i = 2 * i + 2
|
||||
|
||||
let swap a i j =
|
||||
let t = a.(i) in
|
||||
a.(i) <- a.(j);
|
||||
a.(j) <- t
|
||||
|
||||
let rec sift_up a i =
|
||||
if i > 0 && a.(parent i) > a.(i) then begin
|
||||
swap a i (parent i);
|
||||
sift_up a (parent i)
|
||||
end
|
||||
|
||||
let rec sift_down a n i =
|
||||
let l = lchild i and r = rchild i in
|
||||
let smallest = ref i in
|
||||
if l < n && a.(l) < a.(!smallest) then smallest := l;
|
||||
if r < n && a.(r) < a.(!smallest) then smallest := r;
|
||||
if !smallest <> i then begin
|
||||
swap a i !smallest;
|
||||
sift_down a n !smallest
|
||||
end
|
||||
|
||||
let push a size x =
|
||||
a.(!size) <- x;
|
||||
size := !size + 1;
|
||||
sift_up a (!size - 1)
|
||||
|
||||
let pop a size =
|
||||
let m = a.(0) in
|
||||
size := !size - 1;
|
||||
a.(0) <- a.(!size);
|
||||
sift_down a !size 0;
|
||||
m
|
||||
|
||||
;;
|
||||
|
||||
let a = Array.make 20 0 in
|
||||
let s = ref 0 in
|
||||
List.iter (fun x -> push a s x) [9; 4; 7; 1; 8; 3; 5; 2; 6];
|
||||
let total = ref 0 in
|
||||
for _ = 1 to 9 do
|
||||
total := !total * 10 + pop a s
|
||||
done;
|
||||
!total
|
||||
@@ -1,39 +0,0 @@
|
||||
let is_bipartite n adj =
|
||||
let color = Array.make n (-1) in
|
||||
let ok = ref true in
|
||||
let q = Queue.create () in
|
||||
for src = 0 to n - 1 do
|
||||
if color.(src) = -1 then begin
|
||||
color.(src) <- 0;
|
||||
Queue.push src q;
|
||||
while not (Queue.is_empty q) do
|
||||
let u = Queue.pop q in
|
||||
List.iter (fun v ->
|
||||
if color.(v) = -1 then begin
|
||||
color.(v) <- 1 - color.(u);
|
||||
Queue.push v q
|
||||
end else if color.(v) = color.(u) then
|
||||
ok := false
|
||||
) adj.(u)
|
||||
done
|
||||
end
|
||||
done;
|
||||
let zeros = ref 0 in
|
||||
for i = 0 to n - 1 do
|
||||
if color.(i) = 0 then zeros := !zeros + 1
|
||||
done;
|
||||
if !ok then !zeros else -1
|
||||
|
||||
;;
|
||||
|
||||
let n = 7 in
|
||||
let adj = [|
|
||||
[1; 3];
|
||||
[0; 2; 4];
|
||||
[1; 5];
|
||||
[0; 4; 6];
|
||||
[1; 3];
|
||||
[2; 6];
|
||||
[3; 5]
|
||||
|] in
|
||||
is_bipartite n adj
|
||||
@@ -1,13 +0,0 @@
|
||||
let bisect f lo hi =
|
||||
let lo = ref lo and hi = ref hi in
|
||||
for _ = 1 to 50 do
|
||||
let mid = (!lo +. !hi) /. 2.0 in
|
||||
if f mid = 0.0 || f !lo *. f mid < 0.0 then hi := mid
|
||||
else lo := mid
|
||||
done;
|
||||
!lo
|
||||
|
||||
;;
|
||||
|
||||
let r = bisect (fun x -> x *. x -. 2.0) 1.0 2.0 in
|
||||
int_of_float (r *. 100.0)
|
||||
@@ -1,12 +0,0 @@
|
||||
let popcount n =
|
||||
let count = ref 0 in
|
||||
let m = ref n in
|
||||
while !m > 0 do
|
||||
if !m land 1 = 1 then count := !count + 1;
|
||||
m := !m lsr 1
|
||||
done;
|
||||
!count
|
||||
|
||||
;;
|
||||
|
||||
popcount 1023 + popcount 5 + popcount 1024 + popcount 0xff
|
||||
@@ -1,24 +0,0 @@
|
||||
let bowling_score frames =
|
||||
let arr = Array.of_list frames in
|
||||
let n = Array.length arr in
|
||||
let total = ref 0 in
|
||||
let i = ref 0 in
|
||||
let frame = ref 1 in
|
||||
while !frame <= 10 && !i < n do
|
||||
if arr.(!i) = 10 then begin
|
||||
total := !total + 10 + arr.(!i + 1) + arr.(!i + 2);
|
||||
i := !i + 1
|
||||
end else if !i + 1 < n && arr.(!i) + arr.(!i + 1) = 10 then begin
|
||||
total := !total + 10 + arr.(!i + 2);
|
||||
i := !i + 2
|
||||
end else begin
|
||||
total := !total + arr.(!i) + arr.(!i + 1);
|
||||
i := !i + 2
|
||||
end;
|
||||
frame := !frame + 1
|
||||
done;
|
||||
!total
|
||||
|
||||
;;
|
||||
|
||||
bowling_score [10; 7; 3; 9; 0; 10; 0; 8; 8; 2; 0; 6; 10; 10; 10; 8; 1]
|
||||
@@ -1,32 +0,0 @@
|
||||
let bracket_match s =
|
||||
let n = String.length s in
|
||||
let stack = ref [] in
|
||||
let ok = ref true in
|
||||
let i = ref 0 in
|
||||
while !ok && !i < n do
|
||||
let c = s.[!i] in
|
||||
if c = '(' || c = '[' || c = '{' then
|
||||
stack := c :: !stack
|
||||
else if c = ')' || c = ']' || c = '}' then begin
|
||||
match !stack with
|
||||
| [] -> ok := false
|
||||
| top :: rest ->
|
||||
let pair =
|
||||
(c = ')' && top = '(') ||
|
||||
(c = ']' && top = '[') ||
|
||||
(c = '}' && top = '{')
|
||||
in
|
||||
if pair then stack := rest else ok := false
|
||||
end;
|
||||
i := !i + 1
|
||||
done;
|
||||
if !ok && !stack = [] then 1 else 0
|
||||
|
||||
;;
|
||||
|
||||
let strings = ["()"; "[{()}]"; "({[}])"; ""; "(("; "()[](){}"; "(a(b)c)"; "(()"; "])"] in
|
||||
let count = ref 0 in
|
||||
List.iter (fun s ->
|
||||
count := !count + bracket_match s
|
||||
) strings;
|
||||
!count
|
||||
@@ -1,20 +0,0 @@
|
||||
let interpret prog =
|
||||
let mem = Array.make 256 0 in
|
||||
let ptr = ref 0 in
|
||||
let pc = ref 0 in
|
||||
let n = String.length prog in
|
||||
let acc = ref 0 in
|
||||
while !pc < n do
|
||||
let c = prog.[!pc] in
|
||||
(if c = '>' then ptr := !ptr + 1
|
||||
else if c = '<' then ptr := !ptr - 1
|
||||
else if c = '+' then mem.(!ptr) <- mem.(!ptr) + 1
|
||||
else if c = '-' then mem.(!ptr) <- mem.(!ptr) - 1
|
||||
else if c = '.' then acc := !acc + mem.(!ptr));
|
||||
pc := !pc + 1
|
||||
done;
|
||||
!acc
|
||||
|
||||
;;
|
||||
|
||||
interpret "+++++.+++++.+++++.+++++.+++++."
|
||||
@@ -1,27 +0,0 @@
|
||||
let lower_bound arr x =
|
||||
let lo = ref 0 and hi = ref (Array.length arr) in
|
||||
while !lo < !hi do
|
||||
let mid = (!lo + !hi) / 2 in
|
||||
if arr.(mid) < x then lo := mid + 1
|
||||
else hi := mid
|
||||
done;
|
||||
!lo
|
||||
|
||||
let upper_bound arr x =
|
||||
let lo = ref 0 and hi = ref (Array.length arr) in
|
||||
while !lo < !hi do
|
||||
let mid = (!lo + !hi) / 2 in
|
||||
if arr.(mid) <= x then lo := mid + 1
|
||||
else hi := mid
|
||||
done;
|
||||
!lo
|
||||
|
||||
;;
|
||||
|
||||
let a = [| 1; 2; 2; 3; 3; 3; 5; 7; 9 |] in
|
||||
let cnt3 = upper_bound a 3 - lower_bound a 3 in
|
||||
let cnt2 = upper_bound a 2 - lower_bound a 2 in
|
||||
let cnt5 = upper_bound a 5 - lower_bound a 5 in
|
||||
let cnt9 = upper_bound a 9 - lower_bound a 9 in
|
||||
let cnt4 = upper_bound a 4 - lower_bound a 4 in
|
||||
cnt3 * 1000 + cnt2 * 100 + cnt5 * 10 + cnt9 + cnt4
|
||||
@@ -1,25 +0,0 @@
|
||||
let bs_rotated arr target =
|
||||
let lo = ref 0 in
|
||||
let hi = ref (Array.length arr - 1) in
|
||||
let result = ref (-1) in
|
||||
while !lo <= !hi && !result = -1 do
|
||||
let mid = (!lo + !hi) / 2 in
|
||||
if arr.(mid) = target then result := mid
|
||||
else if arr.(!lo) <= arr.(mid) then begin
|
||||
if target >= arr.(!lo) && target < arr.(mid) then
|
||||
hi := mid - 1
|
||||
else
|
||||
lo := mid + 1
|
||||
end else begin
|
||||
if target > arr.(mid) && target <= arr.(!hi) then
|
||||
lo := mid + 1
|
||||
else
|
||||
hi := mid - 1
|
||||
end
|
||||
done;
|
||||
!result
|
||||
|
||||
;;
|
||||
|
||||
let a = [| 4; 5; 6; 7; 0; 1; 2 |] in
|
||||
bs_rotated a 0 + bs_rotated a 7 * 10 + bs_rotated a 3 * 100
|
||||
@@ -1,16 +0,0 @@
|
||||
let bsearch arr target =
|
||||
let n = Array.length arr in
|
||||
let lo = ref 0 and hi = ref (n - 1) in
|
||||
let found = ref (-1) in
|
||||
while !lo <= !hi && !found = -1 do
|
||||
let mid = (!lo + !hi) / 2 in
|
||||
if arr.(mid) = target then found := mid
|
||||
else if arr.(mid) < target then lo := mid + 1
|
||||
else hi := mid - 1
|
||||
done;
|
||||
!found
|
||||
|
||||
;;
|
||||
|
||||
let a = Array.of_list [1;3;5;7;9;11;13;15;17;19;21] in
|
||||
bsearch a 13 + bsearch a 5 + bsearch a 100
|
||||
@@ -1,25 +0,0 @@
|
||||
(* Baseline: binary search tree with insert + in-order traversal *)
|
||||
type 'a tree =
|
||||
| Leaf
|
||||
| Node of 'a * 'a tree * 'a tree
|
||||
;;
|
||||
|
||||
let rec insert x t =
|
||||
match t with
|
||||
| Leaf -> Node (x, Leaf, Leaf)
|
||||
| Node (v, l, r) ->
|
||||
if x < v then Node (v, insert x l, r)
|
||||
else if x > v then Node (v, l, insert x r)
|
||||
else t
|
||||
;;
|
||||
|
||||
let rec inorder t =
|
||||
match t with
|
||||
| Leaf -> []
|
||||
| Node (v, l, r) -> List.append (inorder l) (v :: inorder r)
|
||||
;;
|
||||
|
||||
let from_list xs = List.fold_left (fun t x -> insert x t) Leaf xs ;;
|
||||
|
||||
let t = from_list [5; 3; 8; 1; 4; 7; 9; 2] ;;
|
||||
List.fold_left (fun a b -> a + b) 0 (inorder t)
|
||||
@@ -1,14 +0,0 @@
|
||||
let shift_char c k =
|
||||
let n = Char.code c in
|
||||
if n >= 97 && n <= 122 then
|
||||
Char.chr (((n - 97 + k) mod 26 + 26) mod 26 + 97)
|
||||
else c
|
||||
|
||||
let encode s k =
|
||||
String.init (String.length s) (fun i -> shift_char s.[i] k)
|
||||
;;
|
||||
|
||||
(* ROT13 round-trip: encode (encode "hello" 13) 13 = "hello".
|
||||
Sum the codes of two chars to give a deterministic integer check. *)
|
||||
let r = encode (encode "hello" 13) 13 in
|
||||
Char.code r.[0] + Char.code r.[4]
|
||||
@@ -1,76 +0,0 @@
|
||||
(* Baseline: recursive-descent calculator for "+", "*", parens, ints. *)
|
||||
type expr =
|
||||
| Lit of int
|
||||
| Add of expr * expr
|
||||
| Mul of expr * expr
|
||||
;;
|
||||
|
||||
let parse_input src =
|
||||
let pos = ref 0 in
|
||||
let peek () = if !pos < String.length src then String.get src !pos else "" in
|
||||
let advance () = pos := !pos + 1 in
|
||||
let skip_ws () =
|
||||
while !pos < String.length src && peek () = " " do advance () done
|
||||
in
|
||||
|
||||
let rec parse_atom () =
|
||||
skip_ws () ;
|
||||
if peek () = "(" then begin
|
||||
advance () ;
|
||||
let e = parse_expr () in
|
||||
skip_ws () ;
|
||||
advance () ; (* consume ')' *)
|
||||
e
|
||||
end
|
||||
else
|
||||
let start = !pos in
|
||||
let rec digits () =
|
||||
if !pos < String.length src then
|
||||
let c = peek () in
|
||||
if c >= "0" && c <= "9" then begin advance () ; digits () end
|
||||
else ()
|
||||
in
|
||||
digits () ;
|
||||
let n = Int.of_string (String.sub src start (!pos - start)) in
|
||||
Lit n
|
||||
|
||||
and parse_term () =
|
||||
skip_ws () ;
|
||||
let lhs = ref (parse_atom ()) in
|
||||
let rec loop () =
|
||||
skip_ws () ;
|
||||
if peek () = "*" then begin
|
||||
advance () ;
|
||||
lhs := Mul (!lhs, parse_atom ()) ;
|
||||
loop ()
|
||||
end
|
||||
in
|
||||
loop () ;
|
||||
!lhs
|
||||
|
||||
and parse_expr () =
|
||||
skip_ws () ;
|
||||
let lhs = ref (parse_term ()) in
|
||||
let rec loop () =
|
||||
skip_ws () ;
|
||||
if peek () = "+" then begin
|
||||
advance () ;
|
||||
lhs := Add (!lhs, parse_term ()) ;
|
||||
loop ()
|
||||
end
|
||||
in
|
||||
loop () ;
|
||||
!lhs
|
||||
in
|
||||
parse_expr ()
|
||||
;;
|
||||
|
||||
let rec eval e =
|
||||
match e with
|
||||
| Lit n -> n
|
||||
| Add (a, b) -> eval a + eval b
|
||||
| Mul (a, b) -> eval a * eval b
|
||||
;;
|
||||
|
||||
(* (1 + 2) * 3 + 4 = 9 + 4 = 13 *)
|
||||
eval (parse_input "(1 + 2) * 3 + 4")
|
||||
@@ -1,13 +0,0 @@
|
||||
let catalan n =
|
||||
let dp = Array.make (n + 1) 0 in
|
||||
dp.(0) <- 1;
|
||||
for i = 1 to n do
|
||||
for j = 0 to i - 1 do
|
||||
dp.(i) <- dp.(i) + dp.(j) * dp.(i - 1 - j)
|
||||
done
|
||||
done;
|
||||
dp.(n)
|
||||
|
||||
;;
|
||||
|
||||
catalan 5
|
||||
@@ -1,5 +0,0 @@
|
||||
(* Baseline: closures + curried application *)
|
||||
let make_adder n = fun x -> n + x ;;
|
||||
let add5 = make_adder 5 ;;
|
||||
let add10 = make_adder 10 ;;
|
||||
add5 100 + add10 200
|
||||
@@ -1,15 +0,0 @@
|
||||
let coin_change coins target =
|
||||
let dp = Array.make (target + 1) (target + 1) in
|
||||
dp.(0) <- 0;
|
||||
for i = 1 to target do
|
||||
List.iter (fun c ->
|
||||
if c <= i && dp.(i - c) + 1 < dp.(i) then
|
||||
dp.(i) <- dp.(i - c) + 1
|
||||
) coins
|
||||
done;
|
||||
if dp.(target) > target then -1
|
||||
else dp.(target)
|
||||
|
||||
;;
|
||||
|
||||
coin_change [1; 5; 10; 25] 67
|
||||
@@ -1,16 +0,0 @@
|
||||
let coin_min coins amount =
|
||||
let dp = Array.make (amount + 1) (-1) in
|
||||
dp.(0) <- 0;
|
||||
for i = 1 to amount do
|
||||
List.iter (fun c ->
|
||||
if c <= i && dp.(i - c) >= 0 then begin
|
||||
let cand = dp.(i - c) + 1 in
|
||||
if dp.(i) < 0 || cand < dp.(i) then dp.(i) <- cand
|
||||
end
|
||||
) coins
|
||||
done;
|
||||
dp.(amount)
|
||||
|
||||
;;
|
||||
|
||||
coin_min [1; 5; 10; 25] 67
|
||||
@@ -1,12 +0,0 @@
|
||||
let rec choose k xs =
|
||||
if k = 0 then [[]]
|
||||
else
|
||||
match xs with
|
||||
| [] -> []
|
||||
| h :: rest ->
|
||||
List.map (fun c -> h :: c) (choose (k - 1) rest)
|
||||
@ choose k rest
|
||||
|
||||
;;
|
||||
|
||||
List.length (choose 4 [1; 2; 3; 4; 5; 6; 7; 8; 9])
|
||||
@@ -1,43 +0,0 @@
|
||||
let cross ox oy ax ay bx by =
|
||||
(ax - ox) * (by - oy) - (ay - oy) * (bx - ox)
|
||||
|
||||
let hull_size pts =
|
||||
let n = List.length pts in
|
||||
if n < 3 then n
|
||||
else begin
|
||||
let sorted = List.sort (fun (a, b) (c, d) ->
|
||||
if a <> c then compare a c else compare b d) pts in
|
||||
let arr = Array.of_list sorted in
|
||||
let h = Array.make (2 * n) (0, 0) in
|
||||
let k = ref 0 in
|
||||
for i = 0 to n - 1 do
|
||||
let (xi, yi) = arr.(i) in
|
||||
let cont = ref true in
|
||||
while !cont && !k >= 2 do
|
||||
let (ox, oy) = h.(!k - 2) in
|
||||
let (ax, ay) = h.(!k - 1) in
|
||||
if cross ox oy ax ay xi yi <= 0 then k := !k - 1
|
||||
else cont := false
|
||||
done;
|
||||
h.(!k) <- (xi, yi);
|
||||
k := !k + 1
|
||||
done;
|
||||
let lo = !k + 1 in
|
||||
for i = n - 2 downto 0 do
|
||||
let (xi, yi) = arr.(i) in
|
||||
let cont = ref true in
|
||||
while !cont && !k >= lo do
|
||||
let (ox, oy) = h.(!k - 2) in
|
||||
let (ax, ay) = h.(!k - 1) in
|
||||
if cross ox oy ax ay xi yi <= 0 then k := !k - 1
|
||||
else cont := false
|
||||
done;
|
||||
h.(!k) <- (xi, yi);
|
||||
k := !k + 1
|
||||
done;
|
||||
!k - 1
|
||||
end
|
||||
|
||||
;;
|
||||
|
||||
hull_size [(0, 0); (1, 1); (2, 0); (2, 2); (0, 2); (1, 0); (3, 3); (5, 1)]
|
||||
@@ -1,14 +0,0 @@
|
||||
let count_bits n =
|
||||
let result = Array.make (n + 1) 0 in
|
||||
for i = 1 to n do
|
||||
result.(i) <- result.(i / 2) + (i mod 2)
|
||||
done;
|
||||
let sum = ref 0 in
|
||||
for i = 0 to n do
|
||||
sum := !sum + result.(i)
|
||||
done;
|
||||
!sum
|
||||
|
||||
;;
|
||||
|
||||
count_bits 100
|
||||
@@ -1,13 +0,0 @@
|
||||
let count_ways coins target =
|
||||
let dp = Array.make (target + 1) 0 in
|
||||
dp.(0) <- 1;
|
||||
List.iter (fun c ->
|
||||
for i = c to target do
|
||||
dp.(i) <- dp.(i) + dp.(i - c)
|
||||
done
|
||||
) coins;
|
||||
dp.(target)
|
||||
|
||||
;;
|
||||
|
||||
count_ways [1; 2; 5; 10; 25] 50
|
||||
@@ -1,42 +0,0 @@
|
||||
let count_inv arr =
|
||||
let n = Array.length arr in
|
||||
let temp = Array.make n 0 in
|
||||
let count = ref 0 in
|
||||
let rec merge lo mid hi =
|
||||
let i = ref lo and j = ref (mid + 1) and k = ref lo in
|
||||
while !i <= mid && !j <= hi do
|
||||
if arr.(!i) <= arr.(!j) then begin
|
||||
temp.(!k) <- arr.(!i);
|
||||
i := !i + 1
|
||||
end else begin
|
||||
temp.(!k) <- arr.(!j);
|
||||
count := !count + (mid - !i + 1);
|
||||
j := !j + 1
|
||||
end;
|
||||
k := !k + 1
|
||||
done;
|
||||
while !i <= mid do
|
||||
temp.(!k) <- arr.(!i);
|
||||
i := !i + 1; k := !k + 1
|
||||
done;
|
||||
while !j <= hi do
|
||||
temp.(!k) <- arr.(!j);
|
||||
j := !j + 1; k := !k + 1
|
||||
done;
|
||||
for x = lo to hi do
|
||||
arr.(x) <- temp.(x)
|
||||
done
|
||||
and sort lo hi =
|
||||
if lo < hi then begin
|
||||
let mid = (lo + hi) / 2 in
|
||||
sort lo mid;
|
||||
sort (mid + 1) hi;
|
||||
merge lo mid hi
|
||||
end
|
||||
in
|
||||
sort 0 (n - 1);
|
||||
!count
|
||||
|
||||
;;
|
||||
|
||||
count_inv [|8; 4; 2; 1; 3; 5; 7; 6|]
|
||||
@@ -1,17 +0,0 @@
|
||||
let count_pal s =
|
||||
let n = String.length s in
|
||||
let count = ref 0 in
|
||||
for c = 0 to 2 * n - 2 do
|
||||
let l = ref (c / 2) in
|
||||
let r = ref ((c + 1) / 2) in
|
||||
while !l >= 0 && !r < n && s.[!l] = s.[!r] do
|
||||
count := !count + 1;
|
||||
l := !l - 1;
|
||||
r := !r + 1
|
||||
done
|
||||
done;
|
||||
!count
|
||||
|
||||
;;
|
||||
|
||||
count_pal "aabaa"
|
||||
@@ -1,42 +0,0 @@
|
||||
let n = 6
|
||||
let adj = [|
|
||||
[1; 2];
|
||||
[3];
|
||||
[3; 4];
|
||||
[5];
|
||||
[5];
|
||||
[]
|
||||
|]
|
||||
|
||||
let in_deg = Array.make n 0
|
||||
let paths = Array.make n 0
|
||||
|
||||
let count_paths () =
|
||||
for u = 0 to n - 1 do
|
||||
List.iter (fun v -> in_deg.(v) <- in_deg.(v) + 1) adj.(u)
|
||||
done;
|
||||
let order = ref [] in
|
||||
let q = Queue.create () in
|
||||
for v = 0 to n - 1 do
|
||||
if in_deg.(v) = 0 then Queue.push v q
|
||||
done;
|
||||
while not (Queue.is_empty q) do
|
||||
let u = Queue.pop q in
|
||||
order := u :: !order;
|
||||
List.iter (fun v ->
|
||||
in_deg.(v) <- in_deg.(v) - 1;
|
||||
if in_deg.(v) = 0 then Queue.push v q
|
||||
) adj.(u)
|
||||
done;
|
||||
paths.(0) <- 1;
|
||||
let topo = List.rev !order in
|
||||
List.iter (fun u ->
|
||||
List.iter (fun v ->
|
||||
paths.(v) <- paths.(v) + paths.(u)
|
||||
) adj.(u)
|
||||
) topo;
|
||||
paths.(n - 1)
|
||||
|
||||
;;
|
||||
|
||||
count_paths ()
|
||||
@@ -1,16 +0,0 @@
|
||||
let count_subarr_sum_k arr k =
|
||||
let n = Array.length arr in
|
||||
let prefix = Array.make (n + 1) 0 in
|
||||
for i = 0 to n - 1 do
|
||||
prefix.(i + 1) <- prefix.(i) + arr.(i)
|
||||
done;
|
||||
let count = ref 0 in
|
||||
for i = 0 to n - 1 do
|
||||
for j = i + 1 to n do
|
||||
if prefix.(j) - prefix.(i) = k then count := !count + 1
|
||||
done
|
||||
done;
|
||||
!count
|
||||
;;
|
||||
|
||||
count_subarr_sum_k [| 1; 1; 1; 2; -1; 3; 1; -2; 4 |] 3
|
||||
@@ -1,12 +0,0 @@
|
||||
let sum_second_col text =
|
||||
let lines = String.split_on_char '\n' text in
|
||||
List.fold_left (fun acc line ->
|
||||
let fields = String.split_on_char ',' line in
|
||||
if List.length fields >= 2 then
|
||||
acc + int_of_string (List.nth fields 1)
|
||||
else acc
|
||||
) 0 lines
|
||||
|
||||
;;
|
||||
|
||||
sum_second_col "a,1,extra\nb,2,extra\nc,3,extra\nd,4,extra"
|
||||
@@ -1,24 +0,0 @@
|
||||
let daily_temperatures temps =
|
||||
let n = Array.length temps in
|
||||
let answer = Array.make n 0 in
|
||||
let stack = ref [] in
|
||||
for i = 0 to n - 1 do
|
||||
let cont = ref true in
|
||||
while !cont do
|
||||
match !stack with
|
||||
| top :: rest when temps.(top) < temps.(i) ->
|
||||
answer.(top) <- i - top;
|
||||
stack := rest
|
||||
| _ -> cont := false
|
||||
done;
|
||||
stack := i :: !stack
|
||||
done;
|
||||
let sum = ref 0 in
|
||||
for i = 0 to n - 1 do
|
||||
sum := !sum + answer.(i)
|
||||
done;
|
||||
!sum
|
||||
|
||||
;;
|
||||
|
||||
daily_temperatures [| 73; 74; 75; 71; 69; 72; 76; 73 |]
|
||||
@@ -1,37 +0,0 @@
|
||||
let n = 5
|
||||
|
||||
let edges = [|
|
||||
[(1, 4); (2, 1)];
|
||||
[(3, 1)];
|
||||
[(1, 2); (3, 5)];
|
||||
[(4, 3)];
|
||||
[]
|
||||
|]
|
||||
|
||||
let dijkstra src =
|
||||
let dist = Array.make n 1000000 in
|
||||
dist.(src) <- 0;
|
||||
let visited = Array.make n false in
|
||||
for _ = 0 to n - 1 do
|
||||
let u = ref (-1) in
|
||||
let best = ref 1000000 in
|
||||
for v = 0 to n - 1 do
|
||||
if (not visited.(v)) && dist.(v) < !best then begin
|
||||
best := dist.(v);
|
||||
u := v
|
||||
end
|
||||
done;
|
||||
if !u >= 0 then begin
|
||||
visited.(!u) <- true;
|
||||
List.iter (fun (v, w) ->
|
||||
if dist.(!u) + w < dist.(v) then
|
||||
dist.(v) <- dist.(!u) + w
|
||||
) edges.(!u)
|
||||
end
|
||||
done;
|
||||
dist
|
||||
|
||||
;;
|
||||
|
||||
let d = dijkstra 0 in
|
||||
d.(4)
|
||||
@@ -1,20 +0,0 @@
|
||||
let count_subseq s t =
|
||||
let m = String.length s in
|
||||
let n = String.length t in
|
||||
let dp = Array.init (m + 1) (fun _ -> Array.make (n + 1) 0) in
|
||||
for i = 0 to m do
|
||||
dp.(i).(0) <- 1
|
||||
done;
|
||||
for i = 1 to m do
|
||||
for j = 1 to n do
|
||||
if s.[i - 1] = t.[j - 1] then
|
||||
dp.(i).(j) <- dp.(i - 1).(j) + dp.(i - 1).(j - 1)
|
||||
else
|
||||
dp.(i).(j) <- dp.(i - 1).(j)
|
||||
done
|
||||
done;
|
||||
dp.(m).(n)
|
||||
|
||||
;;
|
||||
|
||||
count_subseq "rabbbit" "rabbit"
|
||||
@@ -1,27 +0,0 @@
|
||||
let word_break s words =
|
||||
let n = String.length s in
|
||||
let dp = Array.make (n + 1) false in
|
||||
dp.(0) <- true;
|
||||
for i = 1 to n do
|
||||
List.iter (fun w ->
|
||||
let wl = String.length w in
|
||||
if i >= wl && dp.(i - wl) then begin
|
||||
let prefix = String.sub s (i - wl) wl in
|
||||
if prefix = w then dp.(i) <- true
|
||||
end
|
||||
) words
|
||||
done;
|
||||
if dp.(n) then 1 else 0
|
||||
|
||||
let count_ok strings words =
|
||||
let count = ref 0 in
|
||||
List.iter (fun s ->
|
||||
count := !count + word_break s words
|
||||
) strings;
|
||||
!count
|
||||
|
||||
;;
|
||||
|
||||
let dict = ["apple"; "pen"; "pine"; "pineapple"; "cats"; "cat"; "and"; "sand"; "dog"] in
|
||||
let inputs = ["applepenapple"; "pineapplepenapple"; "catsanddog"; "catsandog"; "applesand"] in
|
||||
count_ok inputs dict
|
||||
@@ -1,26 +0,0 @@
|
||||
let egg_drop eggs floors =
|
||||
let dp = Array.init (eggs + 1) (fun _ -> Array.make (floors + 1) 0) in
|
||||
for f = 1 to floors do
|
||||
dp.(1).(f) <- f
|
||||
done;
|
||||
for e = 1 to eggs do
|
||||
dp.(e).(0) <- 0;
|
||||
dp.(e).(1) <- 1
|
||||
done;
|
||||
for e = 2 to eggs do
|
||||
for f = 2 to floors do
|
||||
let best = ref 100000000 in
|
||||
for k = 1 to f do
|
||||
let bre = dp.(e - 1).(k - 1) in
|
||||
let sur = dp.(e).(f - k) in
|
||||
let cand = 1 + (if bre > sur then bre else sur) in
|
||||
if cand < !best then best := cand
|
||||
done;
|
||||
dp.(e).(f) <- !best
|
||||
done
|
||||
done;
|
||||
dp.(eggs).(floors)
|
||||
|
||||
;;
|
||||
|
||||
egg_drop 2 36
|
||||
@@ -1,10 +0,0 @@
|
||||
let euler1 limit =
|
||||
let sum = ref 0 in
|
||||
for i = 1 to limit - 1 do
|
||||
if i mod 3 = 0 || i mod 5 = 0 then sum := !sum + i
|
||||
done;
|
||||
!sum
|
||||
|
||||
;;
|
||||
|
||||
euler1 1000
|
||||
@@ -1,22 +0,0 @@
|
||||
let sieve_sum n =
|
||||
let s = Array.make (n + 1) true in
|
||||
s.(0) <- false;
|
||||
s.(1) <- false;
|
||||
for i = 2 to n do
|
||||
if s.(i) then begin
|
||||
let j = ref (i * i) in
|
||||
while !j <= n do
|
||||
s.(!j) <- false;
|
||||
j := !j + i
|
||||
done
|
||||
end
|
||||
done;
|
||||
let total = ref 0 in
|
||||
for i = 2 to n do
|
||||
if s.(i) then total := !total + i
|
||||
done;
|
||||
!total
|
||||
|
||||
;;
|
||||
|
||||
sieve_sum 100
|
||||
@@ -1,25 +0,0 @@
|
||||
let collatz_len n =
|
||||
let m = ref n in
|
||||
let c = ref 0 in
|
||||
while !m > 1 do
|
||||
if !m mod 2 = 0 then m := !m / 2
|
||||
else m := 3 * !m + 1;
|
||||
c := !c + 1
|
||||
done;
|
||||
!c
|
||||
|
||||
let euler14 limit =
|
||||
let best = ref 0 in
|
||||
let best_n = ref 0 in
|
||||
for n = 2 to limit do
|
||||
let l = collatz_len n in
|
||||
if l > !best then begin
|
||||
best := l;
|
||||
best_n := n
|
||||
end
|
||||
done;
|
||||
!best_n
|
||||
|
||||
;;
|
||||
|
||||
euler14 100
|
||||
@@ -1,14 +0,0 @@
|
||||
let euler16 n =
|
||||
let p = ref 1 in
|
||||
for _ = 1 to n do p := !p * 2 done;
|
||||
let sum = ref 0 in
|
||||
let m = ref !p in
|
||||
while !m > 0 do
|
||||
sum := !sum + !m mod 10;
|
||||
m := !m / 10
|
||||
done;
|
||||
!sum
|
||||
|
||||
;;
|
||||
|
||||
euler16 15
|
||||
@@ -1,15 +0,0 @@
|
||||
let euler2 limit =
|
||||
let a = ref 1 in
|
||||
let b = ref 2 in
|
||||
let sum = ref 0 in
|
||||
while !a <= limit do
|
||||
if !a mod 2 = 0 then sum := !sum + !a;
|
||||
let c = !a + !b in
|
||||
a := !b;
|
||||
b := c
|
||||
done;
|
||||
!sum
|
||||
|
||||
;;
|
||||
|
||||
euler2 4000000
|
||||
@@ -1,25 +0,0 @@
|
||||
let div_sum n =
|
||||
let s = ref 1 in
|
||||
let i = ref 2 in
|
||||
while !i * !i <= n do
|
||||
if n mod !i = 0 then begin
|
||||
s := !s + !i;
|
||||
let q = n / !i in
|
||||
if q <> !i then s := !s + q
|
||||
end;
|
||||
i := !i + 1
|
||||
done;
|
||||
if n = 1 then 0 else !s
|
||||
|
||||
let euler21 limit =
|
||||
let total = ref 0 in
|
||||
for a = 2 to limit do
|
||||
let b = div_sum a in
|
||||
if b <> a && b > a && b <= limit && div_sum b = a then
|
||||
total := !total + a + b
|
||||
done;
|
||||
!total
|
||||
|
||||
;;
|
||||
|
||||
euler21 300
|
||||
@@ -1,17 +0,0 @@
|
||||
let euler25 n =
|
||||
let a = ref 1 in
|
||||
let b = ref 1 in
|
||||
let i = ref 2 in
|
||||
let target = ref 1 in
|
||||
for _ = 1 to n - 1 do target := !target * 10 done;
|
||||
while !b < !target do
|
||||
let c = !a + !b in
|
||||
a := !b;
|
||||
b := c;
|
||||
i := !i + 1
|
||||
done;
|
||||
!i
|
||||
|
||||
;;
|
||||
|
||||
euler25 12
|
||||
@@ -1,15 +0,0 @@
|
||||
let euler28 n =
|
||||
let s = ref 1 in
|
||||
let k = ref 1 in
|
||||
for layer = 1 to (n - 1) / 2 do
|
||||
let step = 2 * layer in
|
||||
for _ = 1 to 4 do
|
||||
k := !k + step;
|
||||
s := !s + !k
|
||||
done
|
||||
done;
|
||||
!s
|
||||
|
||||
;;
|
||||
|
||||
euler28 7
|
||||
@@ -1,14 +0,0 @@
|
||||
let euler29 n =
|
||||
let h = Hashtbl.create 64 in
|
||||
for a = 2 to n do
|
||||
for b = 2 to n do
|
||||
let p = ref 1 in
|
||||
for _ = 1 to b do p := !p * a done;
|
||||
Hashtbl.replace h !p ()
|
||||
done
|
||||
done;
|
||||
Hashtbl.length h
|
||||
|
||||
;;
|
||||
|
||||
euler29 5
|
||||
@@ -1,15 +0,0 @@
|
||||
let largest_prime_factor n =
|
||||
let m = ref n in
|
||||
let factor = ref 2 in
|
||||
let largest = ref 0 in
|
||||
while !m > 1 do
|
||||
if !m mod !factor = 0 then begin
|
||||
largest := !factor;
|
||||
m := !m / !factor
|
||||
end else factor := !factor + 1
|
||||
done;
|
||||
!largest
|
||||
|
||||
;;
|
||||
|
||||
largest_prime_factor 13195
|
||||
@@ -1,22 +0,0 @@
|
||||
let pow_digit_sum n p =
|
||||
let m = ref n in
|
||||
let s = ref 0 in
|
||||
while !m > 0 do
|
||||
let d = !m mod 10 in
|
||||
let pd = ref 1 in
|
||||
for _ = 1 to p do pd := !pd * d done;
|
||||
s := !s + !pd;
|
||||
m := !m / 10
|
||||
done;
|
||||
!s
|
||||
|
||||
let euler30 p limit =
|
||||
let total = ref 0 in
|
||||
for n = 2 to limit do
|
||||
if pow_digit_sum n p = n then total := !total + n
|
||||
done;
|
||||
!total
|
||||
|
||||
;;
|
||||
|
||||
euler30 3 999
|
||||
@@ -1,24 +0,0 @@
|
||||
let fact n =
|
||||
let r = ref 1 in
|
||||
for i = 2 to n do r := !r * i done;
|
||||
!r
|
||||
|
||||
let digit_fact_sum n =
|
||||
let m = ref n in
|
||||
let s = ref 0 in
|
||||
while !m > 0 do
|
||||
s := !s + fact (!m mod 10);
|
||||
m := !m / 10
|
||||
done;
|
||||
!s
|
||||
|
||||
let euler34 limit =
|
||||
let total = ref 0 in
|
||||
for n = 3 to limit do
|
||||
if digit_fact_sum n = n then total := !total + n
|
||||
done;
|
||||
!total
|
||||
|
||||
;;
|
||||
|
||||
euler34 2000
|
||||
@@ -1,41 +0,0 @@
|
||||
let pal_dec n =
|
||||
let s = string_of_int n in
|
||||
let len = String.length s in
|
||||
let p = ref true in
|
||||
for i = 0 to len / 2 - 1 do
|
||||
if s.[i] <> s.[len - 1 - i] then p := false
|
||||
done;
|
||||
!p
|
||||
|
||||
let to_binary n =
|
||||
if n = 0 then "0"
|
||||
else
|
||||
let buf = Buffer.create 32 in
|
||||
let m = ref n in
|
||||
let stack = ref [] in
|
||||
while !m > 0 do
|
||||
stack := (!m mod 2) :: !stack;
|
||||
m := !m / 2
|
||||
done;
|
||||
List.iter (fun d -> Buffer.add_string buf (string_of_int d)) !stack;
|
||||
Buffer.contents buf
|
||||
|
||||
let pal_bin n =
|
||||
let s = to_binary n in
|
||||
let len = String.length s in
|
||||
let p = ref true in
|
||||
for i = 0 to len / 2 - 1 do
|
||||
if s.[i] <> s.[len - 1 - i] then p := false
|
||||
done;
|
||||
!p
|
||||
|
||||
let euler36 limit =
|
||||
let sum = ref 0 in
|
||||
for n = 1 to limit - 1 do
|
||||
if pal_dec n && pal_bin n then sum := !sum + n
|
||||
done;
|
||||
!sum
|
||||
|
||||
;;
|
||||
|
||||
euler36 1000
|
||||
@@ -1,22 +0,0 @@
|
||||
let euler40 () =
|
||||
let buf = Buffer.create 4096 in
|
||||
let len = ref 0 in
|
||||
let i = ref 1 in
|
||||
while !len < 1500 do
|
||||
let s = string_of_int !i in
|
||||
Buffer.add_string buf s;
|
||||
len := !len + String.length s;
|
||||
i := !i + 1
|
||||
done;
|
||||
let s = Buffer.contents buf in
|
||||
let prod = ref 1 in
|
||||
let positions = [1; 10; 100; 1000] in
|
||||
List.iter (fun p ->
|
||||
let c = s.[p - 1] in
|
||||
prod := !prod * (Char.code c - Char.code '0')
|
||||
) positions;
|
||||
!prod
|
||||
|
||||
;;
|
||||
|
||||
euler40 ()
|
||||
@@ -1,21 +0,0 @@
|
||||
let is_pal n =
|
||||
let s = string_of_int n in
|
||||
let len = String.length s in
|
||||
let p = ref true in
|
||||
for i = 0 to len / 2 - 1 do
|
||||
if s.[i] <> s.[len - 1 - i] then p := false
|
||||
done;
|
||||
!p
|
||||
|
||||
let euler4 lo hi =
|
||||
let m = ref 0 in
|
||||
for a = lo to hi do
|
||||
for b = a to hi do
|
||||
let p = a * b in
|
||||
if p > !m && is_pal p then m := p
|
||||
done
|
||||
done;
|
||||
!m
|
||||
;;
|
||||
|
||||
euler4 10 99
|
||||
@@ -1,11 +0,0 @@
|
||||
let rec gcd a b = if b = 0 then a else gcd b (a mod b)
|
||||
let lcm a b = a * b / gcd a b
|
||||
let euler5 n =
|
||||
let r = ref 1 in
|
||||
for i = 2 to n do
|
||||
r := lcm !r i
|
||||
done;
|
||||
!r
|
||||
;;
|
||||
|
||||
euler5 20
|
||||
@@ -1,12 +0,0 @@
|
||||
let euler6 n =
|
||||
let sum = ref 0 in
|
||||
let sum_sq = ref 0 in
|
||||
for i = 1 to n do
|
||||
sum := !sum + i;
|
||||
sum_sq := !sum_sq + i * i
|
||||
done;
|
||||
!sum * !sum - !sum_sq
|
||||
|
||||
;;
|
||||
|
||||
euler6 100
|
||||
@@ -1,22 +0,0 @@
|
||||
let nth_prime n =
|
||||
let count = ref 0 in
|
||||
let i = ref 1 in
|
||||
let result = ref 0 in
|
||||
while !count < n do
|
||||
i := !i + 1;
|
||||
let p = ref true in
|
||||
let j = ref 2 in
|
||||
while !j * !j <= !i && !p do
|
||||
if !i mod !j = 0 then p := false;
|
||||
j := !j + 1
|
||||
done;
|
||||
if !p then begin
|
||||
count := !count + 1;
|
||||
if !count = n then result := !i
|
||||
end
|
||||
done;
|
||||
!result
|
||||
|
||||
;;
|
||||
|
||||
nth_prime 100
|
||||
@@ -1,17 +0,0 @@
|
||||
let euler9 () =
|
||||
let result = ref 0 in
|
||||
for a = 1 to 333 do
|
||||
let num = 500000 - 1000 * a in
|
||||
let den = 1000 - a in
|
||||
if num mod den = 0 then begin
|
||||
let b = num / den in
|
||||
if b > a then
|
||||
let c = 1000 - a - b in
|
||||
if c > b then result := a * b * c
|
||||
end
|
||||
done;
|
||||
!result
|
||||
|
||||
;;
|
||||
|
||||
euler9 ()
|
||||
@@ -1,17 +0,0 @@
|
||||
(* Baseline: exception declaration + raise + try-with *)
|
||||
exception NegArg of int ;;
|
||||
let safe_sqrt n =
|
||||
if n < 0 then raise (NegArg n)
|
||||
else
|
||||
begin
|
||||
let rec find_sqrt i =
|
||||
if i * i > n then i - 1
|
||||
else find_sqrt (i + 1)
|
||||
in find_sqrt 0
|
||||
end ;;
|
||||
let result =
|
||||
try
|
||||
safe_sqrt 16
|
||||
with
|
||||
| NegArg _ -> 0 ;;
|
||||
result
|
||||
@@ -1,16 +0,0 @@
|
||||
exception Negative of int
|
||||
|
||||
let safe_sqrt n =
|
||||
if n < 0 then raise (Negative n)
|
||||
else
|
||||
let g = ref 1 in
|
||||
while !g * !g < n do g := !g + 1 done;
|
||||
!g
|
||||
|
||||
let try_sqrt n =
|
||||
try safe_sqrt n with
|
||||
| Negative x -> -x
|
||||
|
||||
;;
|
||||
|
||||
try_sqrt 16 + try_sqrt 25 + try_sqrt (-7) + try_sqrt 100
|
||||
@@ -1,207 +0,0 @@
|
||||
{
|
||||
"abundant.ml": 21,
|
||||
"activity_select.ml": 4,
|
||||
"ackermann.ml": 125,
|
||||
"adler32.ml": 300286872,
|
||||
"anagram_check.ml": 2,
|
||||
"anagram_groups.ml": 3,
|
||||
"anagrams.ml": 3,
|
||||
"atm.ml": 120,
|
||||
"bag.ml": 3,
|
||||
"bowling.ml": 167,
|
||||
"bf_full.ml": 6,
|
||||
"bisect.ml": 141,
|
||||
"bigint_add.ml": 28,
|
||||
"binary_heap.ml": 123456789,
|
||||
"bipartite.ml": 4,
|
||||
"bits.ml": 21,
|
||||
"balance.ml": 3,
|
||||
"bracket_match.ml": 5,
|
||||
"base_n.ml": 17,
|
||||
"bfs.ml": 6,
|
||||
"bfs_grid.ml": 8,
|
||||
"btree.ml": 39,
|
||||
"brainfuck.ml": 75,
|
||||
"bs_bounds.ml": 3211,
|
||||
"bs_rotated.ml": -66,
|
||||
"bsearch.ml": 7,
|
||||
"caesar.ml": 215,
|
||||
"calc.ml": 13,
|
||||
"catalan.ml": 42,
|
||||
"closures.ml": 315,
|
||||
"combinations.ml": 126,
|
||||
"convex_hull.ml": 5,
|
||||
"coin_change.ml": 6,
|
||||
"coin_min.ml": 6,
|
||||
"count_bits.ml": 319,
|
||||
"count_change.ml": 406,
|
||||
"count_paths_dag.ml": 3,
|
||||
"count_inversions.ml": 12,
|
||||
"count_palindromes.ml": 9,
|
||||
"count_subarrays_k.ml": 7,
|
||||
"csv.ml": 10,
|
||||
"daily_temperatures.ml": 10,
|
||||
"egg_drop.ml": 8,
|
||||
"dijkstra.ml": 7,
|
||||
"dp_word_break.ml": 4,
|
||||
"distinct_subseq.ml": 3,
|
||||
"exception_handle.ml": 4,
|
||||
"exception_user.ml": 26,
|
||||
"euler1.ml": 233168,
|
||||
"euler16.ml": 26,
|
||||
"euler10.ml": 1060,
|
||||
"euler14.ml": 97,
|
||||
"euler2.ml": 4613732,
|
||||
"euler21_small.ml": 504,
|
||||
"euler25.ml": 55,
|
||||
"euler28.ml": 261,
|
||||
"euler29_small.ml": 15,
|
||||
"euler30_cube.ml": 1301,
|
||||
"euler34_small.ml": 145,
|
||||
"euler36.ml": 1772,
|
||||
"euler40_small.ml": 15,
|
||||
"euler3.ml": 29,
|
||||
"euler4_small.ml": 9009,
|
||||
"euler5.ml": 232792560,
|
||||
"euler6.ml": 25164150,
|
||||
"euler7.ml": 541,
|
||||
"euler9.ml": 31875000,
|
||||
"expr_eval.ml": 16,
|
||||
"expr_simp.ml": 22,
|
||||
"factorial.ml": 3628800,
|
||||
"fenwick_tree.ml": 228,
|
||||
"fib_doubling.ml": 102334155,
|
||||
"fib_mod.ml": 391360,
|
||||
"fraction.ml": 7,
|
||||
"frequency.ml": 5,
|
||||
"gas_station.ml": 3,
|
||||
"gcd_lcm.ml": 60,
|
||||
"gray_code.ml": 136,
|
||||
"grep_count.ml": 3,
|
||||
"grid_paths.ml": 210,
|
||||
"group_consec.ml": 53,
|
||||
"hailstone.ml": 111,
|
||||
"harshad.ml": 33,
|
||||
"hamming.ml": 4,
|
||||
"hanoi.ml": 1023,
|
||||
"hist.ml": 75,
|
||||
"house_robber.ml": 22,
|
||||
"histogram_area.ml": 10,
|
||||
"huffman.ml": 224,
|
||||
"int_sqrt.ml": 1027,
|
||||
"interval_overlap.ml": 6,
|
||||
"is_prime.ml": 25,
|
||||
"island_count.ml": 5,
|
||||
"fizz_classifier.ml": 540,
|
||||
"fizzbuzz.ml": 57,
|
||||
"flatten_tree.ml": 28,
|
||||
"flood_fill.ml": 7,
|
||||
"floyd_cycle.ml": 8,
|
||||
"floyd_warshall.ml": 9,
|
||||
"lis.ml": 6,
|
||||
"list_ops.ml": 30,
|
||||
"lps_dp.ml": 7,
|
||||
"lru_cache.ml": 499,
|
||||
"luhn.ml": 2,
|
||||
"magic_square.ml": 65,
|
||||
"mat_mul.ml": 621,
|
||||
"matrix_power.ml": 832040,
|
||||
"max_path_tree.ml": 11,
|
||||
"max_product3.ml": 300,
|
||||
"max_run.ml": 5,
|
||||
"mod_inverse.ml": 27,
|
||||
"josephus.ml": 11,
|
||||
"json_pretty.ml": 24,
|
||||
"kadane.ml": 6,
|
||||
"kmp.ml": 5,
|
||||
"kth_two.ml": 8,
|
||||
"knapsack.ml": 36,
|
||||
"lambda_calc.ml": 7,
|
||||
"lcs.ml": 4,
|
||||
"majority_vote.ml": 4,
|
||||
"manacher.ml": 7,
|
||||
"lev_iter.ml": 16,
|
||||
"levenshtein.ml": 11,
|
||||
"memo_fib.ml": 75025,
|
||||
"mortgage.ml": 1073,
|
||||
"mst_kruskal.ml": 11,
|
||||
"merge_intervals.ml": 12,
|
||||
"min_meeting_rooms.ml": 4,
|
||||
"merge_sort.ml": 44,
|
||||
"merge_two.ml": 441,
|
||||
"min_cost_path.ml": 12,
|
||||
"min_jumps.ml": 4,
|
||||
"min_subarr_target.ml": 2,
|
||||
"module_use.ml": 3,
|
||||
"monotonic.ml": 4,
|
||||
"newton_sqrt.ml": 1414,
|
||||
"next_greater.ml": 153,
|
||||
"next_permutation.ml": 119,
|
||||
"number_words.ml": 106,
|
||||
"mutable_record.ml": 10,
|
||||
"option_match.ml": 5,
|
||||
"palindrome.ml": 4,
|
||||
"palindrome_part.ml": 1,
|
||||
"palindrome_sum.ml": 49500,
|
||||
"paren_depth.ml": 7,
|
||||
"partition.ml": 3025,
|
||||
"partition_count.ml": 176,
|
||||
"pancake_sort.ml": 910,
|
||||
"pascal.ml": 252,
|
||||
"peano.ml": 30,
|
||||
"perfect.ml": 3,
|
||||
"permutations_gen.ml": 12,
|
||||
"pi_leibniz.ml": 314,
|
||||
"prefix_sum.ml": 66,
|
||||
"pretty_table.ml": 64,
|
||||
"poly_stack.ml": 5,
|
||||
"polygon_area.ml": 32,
|
||||
"pow_mod.ml": 738639,
|
||||
"powerset_target.ml": 20,
|
||||
"prime_factors.ml": 17,
|
||||
"pythagorean.ml": 16,
|
||||
"queens.ml": 2,
|
||||
"quickselect.ml": 5,
|
||||
"quicksort.ml": 44,
|
||||
"radix_sort.ml": 802002,
|
||||
"roman.ml": 44,
|
||||
"rolling_hash.ml": 6,
|
||||
"regex_simple.ml": 7,
|
||||
"reverse_int.ml": 54329,
|
||||
"rpn.ml": 9,
|
||||
"run_decode.ml": 21,
|
||||
"run_length.ml": 11,
|
||||
"safe_div.ml": 20,
|
||||
"segment_tree.ml": 4232,
|
||||
"shuffle.ml": 55,
|
||||
"simpson_int.ml": 10000,
|
||||
"stable_unique.ml": 46,
|
||||
"stock_two.ml": 6,
|
||||
"subseq_check.ml": 3,
|
||||
"tail_factorial.ml": 479001600,
|
||||
"task_scheduler.ml": 7,
|
||||
"tarjan_scc.ml": 4,
|
||||
"subset_sum.ml": 8,
|
||||
"tic_tac_toe.ml": 1,
|
||||
"topo_dfs.ml": 24135,
|
||||
"topo_sort.ml": 6,
|
||||
"wildcard_match.ml": 6,
|
||||
"word_freq.ml": 8,
|
||||
"xor_cipher.ml": 601,
|
||||
"zerosafe.ml": 28,
|
||||
"zigzag.ml": 55,
|
||||
"zip_unzip.ml": 1000,
|
||||
"sieve.ml": 15,
|
||||
"sum_squares.ml": 385,
|
||||
"tree_depth.ml": 4,
|
||||
"trapping_rain.ml": 6,
|
||||
"triangle.ml": 11,
|
||||
"trie.ml": 6,
|
||||
"triangle_div.ml": 120,
|
||||
"twosum.ml": 5,
|
||||
"union_find.ml": 4,
|
||||
"unique_paths_obs.ml": 3,
|
||||
"unique_set.ml": 9,
|
||||
"validate.ml": 417,
|
||||
"word_count.ml": 3
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
(* Baseline: a tiny expression evaluator using ADTs + match *)
|
||||
type expr =
|
||||
| Lit of int
|
||||
| Add of expr * expr
|
||||
| Mul of expr * expr
|
||||
| Neg of expr
|
||||
;;
|
||||
|
||||
let rec eval e =
|
||||
match e with
|
||||
| Lit n -> n
|
||||
| Add (a, b) -> eval a + eval b
|
||||
| Mul (a, b) -> eval a * eval b
|
||||
| Neg x -> 0 - eval x
|
||||
;;
|
||||
|
||||
(* (1 + 2) * (3 + 4) - 5 = 21 - 5 = 16 *)
|
||||
eval
|
||||
(Add (Mul (Add (Lit 1, Lit 2), Add (Lit 3, Lit 4)), Neg (Lit 5)))
|
||||
@@ -1,33 +0,0 @@
|
||||
type expr =
|
||||
| Num of int
|
||||
| Add of expr * expr
|
||||
| Mul of expr * expr
|
||||
|
||||
let rec simp e =
|
||||
match e with
|
||||
| Num n -> Num n
|
||||
| Add (a, b) ->
|
||||
(match (simp a, simp b) with
|
||||
| (Num 0, x) -> x
|
||||
| (x, Num 0) -> x
|
||||
| (Num n, Num m) -> Num (n + m)
|
||||
| (a', b') -> Add (a', b'))
|
||||
| Mul (a, b) ->
|
||||
(match (simp a, simp b) with
|
||||
| (Num 0, _) -> Num 0
|
||||
| (_, Num 0) -> Num 0
|
||||
| (Num 1, x) -> x
|
||||
| (x, Num 1) -> x
|
||||
| (Num n, Num m) -> Num (n * m)
|
||||
| (a', b') -> Mul (a', b'))
|
||||
|
||||
let rec eval e =
|
||||
match e with
|
||||
| Num n -> n
|
||||
| Add (a, b) -> eval a + eval b
|
||||
| Mul (a, b) -> eval a * eval b
|
||||
|
||||
;;
|
||||
|
||||
let e = Add (Mul (Num 3, Num 5), Add (Num 0, Mul (Num 1, Num 7))) in
|
||||
eval (simp e)
|
||||
@@ -1,4 +0,0 @@
|
||||
(* Baseline: factorial via let-rec *)
|
||||
let rec fact n =
|
||||
if n = 0 then 1 else n * fact (n - 1) ;;
|
||||
fact 10
|
||||
@@ -1,33 +0,0 @@
|
||||
let n = 8
|
||||
|
||||
let bit = Array.make (n + 1) 0
|
||||
|
||||
let lowbit i =
|
||||
let r = ref 1 in
|
||||
while !r * 2 <= i && i mod (!r * 2) = 0 do
|
||||
r := !r * 2
|
||||
done;
|
||||
!r
|
||||
|
||||
let rec update i delta =
|
||||
if i <= n then begin
|
||||
bit.(i) <- bit.(i) + delta;
|
||||
update (i + lowbit i) delta
|
||||
end
|
||||
|
||||
let rec prefix_sum i =
|
||||
if i <= 0 then 0
|
||||
else bit.(i) + prefix_sum (i - lowbit i)
|
||||
|
||||
let range_sum l r = prefix_sum r - prefix_sum (l - 1)
|
||||
|
||||
;;
|
||||
|
||||
let a = [| 1; 3; 5; 7; 9; 11; 13; 15 |] in
|
||||
for i = 0 to n - 1 do
|
||||
update (i + 1) a.(i)
|
||||
done;
|
||||
let total = prefix_sum n in
|
||||
update 1 100;
|
||||
let after = prefix_sum n in
|
||||
total + after
|
||||
@@ -1,14 +0,0 @@
|
||||
let rec fib_pair n =
|
||||
if n = 0 then (0, 1)
|
||||
else
|
||||
let (a, b) = fib_pair (n / 2) in
|
||||
let c = a * (2 * b - a) in
|
||||
let d = a * a + b * b in
|
||||
if n mod 2 = 0 then (c, d)
|
||||
else (d, c + d)
|
||||
|
||||
let fib n = let (f, _) = fib_pair n in f
|
||||
|
||||
;;
|
||||
|
||||
fib 40
|
||||
@@ -1,13 +0,0 @@
|
||||
let fib_mod n m =
|
||||
let a = ref 0 in
|
||||
let b = ref 1 in
|
||||
for _ = 1 to n do
|
||||
let c = (!a + !b) mod m in
|
||||
a := !b;
|
||||
b := c
|
||||
done;
|
||||
!a
|
||||
|
||||
;;
|
||||
|
||||
fib_mod 100 1000003
|
||||
@@ -1,21 +0,0 @@
|
||||
let classify n =
|
||||
let by3 = n mod 3 = 0 in
|
||||
let by5 = n mod 5 = 0 in
|
||||
if by3 && by5 then `FizzBuzz
|
||||
else if by3 then `Fizz
|
||||
else if by5 then `Buzz
|
||||
else `Num n
|
||||
|
||||
let score x = match x with
|
||||
| `FizzBuzz -> 100
|
||||
| `Fizz -> 10
|
||||
| `Buzz -> 5
|
||||
| `Num n -> n
|
||||
|
||||
;;
|
||||
|
||||
let total = ref 0 in
|
||||
for i = 1 to 30 do
|
||||
total := !total + score (classify i)
|
||||
done;
|
||||
!total
|
||||
@@ -1,17 +0,0 @@
|
||||
(* Baseline: fizzbuzz returning a list of strings *)
|
||||
let fizzbuzz n =
|
||||
let acc = ref [] in
|
||||
for i = 1 to n do
|
||||
let s =
|
||||
if i mod 15 = 0 then "FizzBuzz"
|
||||
else if i mod 3 = 0 then "Fizz"
|
||||
else if i mod 5 = 0 then "Buzz"
|
||||
else Int.to_string i
|
||||
in
|
||||
acc := s :: !acc
|
||||
done ;
|
||||
List.rev !acc
|
||||
;;
|
||||
|
||||
(* Concatenated for a deterministic check value via String.length *)
|
||||
String.length (String.concat "," (fizzbuzz 15))
|
||||
@@ -1,17 +0,0 @@
|
||||
type 'a tree = Leaf of 'a | Node of 'a tree list
|
||||
|
||||
let rec flatten t =
|
||||
match t with
|
||||
| Leaf x -> [x]
|
||||
| Node ts -> List.concat (List.map flatten ts)
|
||||
|
||||
;;
|
||||
|
||||
let t = Node [
|
||||
Leaf 1;
|
||||
Node [Leaf 2; Leaf 3];
|
||||
Node [Node [Leaf 4]; Leaf 5; Leaf 6];
|
||||
Leaf 7
|
||||
]
|
||||
in
|
||||
List.fold_left (+) 0 (flatten t)
|
||||
@@ -1,38 +0,0 @@
|
||||
let h = 5
|
||||
let w = 5
|
||||
|
||||
let grid = [|
|
||||
[| 1; 1; 0; 1; 1 |];
|
||||
[| 1; 0; 0; 0; 1 |];
|
||||
[| 0; 0; 1; 0; 0 |];
|
||||
[| 1; 1; 1; 1; 0 |];
|
||||
[| 0; 0; 0; 1; 1 |]
|
||||
|]
|
||||
|
||||
let rec flood visited r c =
|
||||
if r < 0 || r >= h || c < 0 || c >= w then 0
|
||||
else if visited.(r).(c) || grid.(r).(c) = 0 then 0
|
||||
else begin
|
||||
visited.(r).(c) <- true;
|
||||
1 + flood visited (r - 1) c
|
||||
+ flood visited (r + 1) c
|
||||
+ flood visited r (c - 1)
|
||||
+ flood visited r (c + 1)
|
||||
end
|
||||
|
||||
let largest_component () =
|
||||
let visited = Array.init h (fun _ -> Array.make w false) in
|
||||
let best = ref 0 in
|
||||
for r = 0 to h - 1 do
|
||||
for c = 0 to w - 1 do
|
||||
if grid.(r).(c) = 1 && not visited.(r).(c) then begin
|
||||
let s = flood visited r c in
|
||||
if s > !best then best := s
|
||||
end
|
||||
done
|
||||
done;
|
||||
!best
|
||||
|
||||
;;
|
||||
|
||||
largest_component ()
|
||||
@@ -1,29 +0,0 @@
|
||||
let f x = (x * 2 + 5) mod 17
|
||||
|
||||
let floyd_cycle x0 =
|
||||
let slow = ref x0 in
|
||||
let fast = ref x0 in
|
||||
let meet = ref false in
|
||||
while not !meet do
|
||||
slow := f !slow;
|
||||
fast := f (f !fast);
|
||||
if !slow = !fast then meet := true
|
||||
done;
|
||||
slow := x0;
|
||||
let mu = ref 0 in
|
||||
while !slow <> !fast do
|
||||
slow := f !slow;
|
||||
fast := f !fast;
|
||||
mu := !mu + 1
|
||||
done;
|
||||
let lam = ref 1 in
|
||||
fast := f !slow;
|
||||
while !slow <> !fast do
|
||||
fast := f !fast;
|
||||
lam := !lam + 1
|
||||
done;
|
||||
!mu * 100 + !lam
|
||||
|
||||
;;
|
||||
|
||||
floyd_cycle 1
|
||||
@@ -1,26 +0,0 @@
|
||||
let inf_int = 1000000
|
||||
|
||||
let floyd n graph =
|
||||
let d = Array.init n (fun i ->
|
||||
Array.init n (fun j -> graph.(i).(j))) in
|
||||
for k = 0 to n - 1 do
|
||||
for i = 0 to n - 1 do
|
||||
for j = 0 to n - 1 do
|
||||
if d.(i).(k) + d.(k).(j) < d.(i).(j) then
|
||||
d.(i).(j) <- d.(i).(k) + d.(k).(j)
|
||||
done
|
||||
done
|
||||
done;
|
||||
d
|
||||
|
||||
;;
|
||||
|
||||
let n = 4 in
|
||||
let g = Array.init n (fun _ -> Array.make n inf_int) in
|
||||
for i = 0 to n - 1 do g.(i).(i) <- 0 done;
|
||||
g.(0).(1) <- 5;
|
||||
g.(0).(3) <- 10;
|
||||
g.(1).(2) <- 3;
|
||||
g.(2).(3) <- 1;
|
||||
let d = floyd n g in
|
||||
d.(0).(3)
|
||||
@@ -1,20 +0,0 @@
|
||||
type frac = { num : int; den : int }
|
||||
|
||||
let rec gcd a b = if b = 0 then a else gcd b (a mod b)
|
||||
|
||||
let make n d =
|
||||
let g = gcd (abs n) (abs d) in
|
||||
if d < 0 then { num = -n / g; den = -d / g }
|
||||
else { num = n / g; den = d / g }
|
||||
|
||||
let add x y =
|
||||
make (x.num * y.den + y.num * x.den) (x.den * y.den)
|
||||
|
||||
let mul x y = make (x.num * y.num) (x.den * y.den)
|
||||
|
||||
;;
|
||||
|
||||
let r = add (make 1 2) (make 1 3) in
|
||||
let s = mul (make 2 3) (make 3 4) in
|
||||
let t = add r s in
|
||||
t.num + t.den
|
||||
@@ -1,18 +0,0 @@
|
||||
let count_chars s =
|
||||
let t = Hashtbl.create 8 in
|
||||
for i = 0 to String.length s - 1 do
|
||||
let c = s.[i] in
|
||||
let n = match Hashtbl.find_opt t c with
|
||||
| Some v -> v + 1
|
||||
| None -> 1
|
||||
in
|
||||
Hashtbl.replace t c n
|
||||
done;
|
||||
t
|
||||
|
||||
let max_count t =
|
||||
Hashtbl.fold (fun _ v acc -> if v > acc then v else acc) t 0
|
||||
|
||||
;;
|
||||
|
||||
max_count (count_chars "abracadabra")
|
||||
@@ -1,21 +0,0 @@
|
||||
let gas_circuit gas cost =
|
||||
let n = Array.length gas in
|
||||
let total = ref 0 in
|
||||
let curr = ref 0 in
|
||||
let start = ref 0 in
|
||||
for i = 0 to n - 1 do
|
||||
let diff = gas.(i) - cost.(i) in
|
||||
total := !total + diff;
|
||||
curr := !curr + diff;
|
||||
if !curr < 0 then begin
|
||||
start := i + 1;
|
||||
curr := 0
|
||||
end
|
||||
done;
|
||||
if !total < 0 then -1 else !start
|
||||
|
||||
;;
|
||||
|
||||
let gas = [| 1; 2; 3; 4; 5 |] in
|
||||
let cost = [| 3; 4; 5; 1; 2 |] in
|
||||
gas_circuit gas cost
|
||||
@@ -1,6 +0,0 @@
|
||||
let rec gcd a b = if b = 0 then a else gcd b (a mod b)
|
||||
|
||||
let lcm a b = a * b / gcd a b
|
||||
;;
|
||||
|
||||
gcd 36 48 + lcm 4 6 + lcm 12 18
|
||||
@@ -1,12 +0,0 @@
|
||||
let gray n =
|
||||
let m = 1 lsl n in
|
||||
let result = Array.make m 0 in
|
||||
for i = 0 to m - 1 do
|
||||
result.(i) <- i lxor (i lsr 1)
|
||||
done;
|
||||
result
|
||||
|
||||
;;
|
||||
|
||||
let g = gray 4 in
|
||||
Array.fold_left (+) 0 g + Array.length g
|
||||
@@ -1,17 +0,0 @@
|
||||
let rec str_contains s sub i =
|
||||
let nl = String.length s in
|
||||
let sl = String.length sub in
|
||||
if i + sl > nl then false
|
||||
else if String.sub s i sl = sub then true
|
||||
else str_contains s sub (i + 1)
|
||||
|
||||
let count_matching needle text =
|
||||
let lines = String.split_on_char '\n' text in
|
||||
List.fold_left (fun acc line ->
|
||||
if str_contains line needle 0 then acc + 1
|
||||
else acc
|
||||
) 0 lines
|
||||
|
||||
;;
|
||||
|
||||
count_matching "fox" "the quick brown fox\nfox runs fast\nthe dog\nfoxes are clever"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user