refactor into modules

This commit is contained in:
mehbark 2023-01-13 17:21:19 -05:00
parent aaf9d826c6
commit 376ae6035e
4 changed files with 223 additions and 214 deletions

68
src/matrix/base.clj Normal file
View file

@ -0,0 +1,68 @@
(ns matrix.base)
(defn to-vec
[v]
(apply vector v))
(defn swap-rows
"Swap rows n and m of the given matrix, 0-indexed"
[matrix n m]
(assoc matrix
m (matrix n)
n (matrix m)))
(defn add-rows-with-mul
"n * row from + row to"
[matrix n from to]
(let [from-row (get matrix from)
to-row (get matrix to)
new-row (-> (map (fn [f t] (+ (* n f) t))
from-row to-row)
to-vec)]
(assoc matrix
to new-row)))
(defn add-rows
"row from + row to"
[matrix from to]
(add-rows-with-mul matrix 1 from to))
(defn mul-row
"n * row"
[matrix n row]
(update matrix
row #(to-vec (map (partial * n) %))))
(defn row-with-fill
"Creates a vector row with the given width and a specified fill (default 0)"
([width] (row-with-fill width 0))
([width fill]
(apply vector
(map (constantly fill)
(range 0 width)))))
(defn empty-matrix
"Creates a height x width matrix with the given default value (default 0)"
([height width] (empty-matrix height width 0))
([height width fill]
(let [row (row-with-fill width fill)]
(->> (range 0 height)
(map (constantly row))
(apply vector)))))
(defn id-matrix
"Creates the id matrix of the given width"
[width]
(let [base (empty-matrix width width 0)]
(->> base
(map (fn [i row] (assoc row i 1))
(range 0 width))
(apply vector))))
(def matrix-width
"Get the width of a matrix (assumes that all rows are the same size, as they should be!)"
(comp count first))
(def matrix-height
"Get the height of a matrix"
count)

View file

@ -1,220 +1,8 @@
(ns matrix.core
(:require [clojure.string :as str])
(:require [matrix.base :refer :all])
(:require [matrix.interactive :refer :all])
(:gen-class))
(defn to-vec
[v]
(apply vector v))
(defn swap-rows
"Swap rows n and m of the given matrix, 0-indexed"
[matrix n m]
(assoc matrix
m (matrix n)
n (matrix m)))
(defn add-rows-with-mul
"n * row from + row to"
[matrix n from to]
(let [from-row (get matrix from)
to-row (get matrix to)
new-row (-> (map (fn [f t] (+ (* n f) t))
from-row to-row)
to-vec)]
(assoc matrix
to new-row)))
(defn add-rows
"row from + row to"
[matrix from to]
(add-rows-with-mul matrix 1 from to))
(defn mul-row
"n * row"
[matrix n row]
(update matrix
row #(to-vec (map (partial * n) %))))
(defn row-with-fill
"Creates a vector row with the given width and a specified fill (default 0)"
([width] (row-with-fill width 0))
([width fill]
(apply vector
(map (constantly fill)
(range 0 width)))))
(defn empty-matrix
"Creates a height x width matrix with the given default value (default 0)"
([height width] (empty-matrix height width 0))
([height width fill]
(let [row (row-with-fill width fill)]
(->> (range 0 height)
(map (constantly row))
(apply vector)))))
(defn id-matrix
"Creates the id matrix of the given width"
[width]
(let [base (empty-matrix width width 0)]
(->> base
(map (fn [i row] (assoc row i 1))
(range 0 width))
(apply vector))))
(def matrix-width
"Get the width of a matrix (assumes that all rows are the same size, as they should be!)"
(comp count first))
(def matrix-height
"Get the height of a matrix"
count)
(defn surround-with
"Surround a string with the given left and right"
([left right] #(surround-with left right %))
([left right s] (str left s right)))
(defn add-before-last
[what where]
(concat (take (dec (count where)) where)
[what]
[(last where)]))
(defn draw-row
"Takes a seq of strs and a max length and draws a row.
Optionally takes a left and right to surround with"
([seq max-len add-bar]
(let [fmt-str (str "%" max-len "s")]
(->> seq
(map (partial format fmt-str))
((if add-bar
(partial add-before-last "│")
identity))
(str/join " "))))
([seq max-len add-bar left right]
((surround-with left right) (draw-row seq max-len add-bar))))
(defn max-str-len
"Returns the maximum length of a stringified seq"
[seq]
(->> seq
(map str)
(map count)
(apply max 0)))
(defn max-str-len-matrix
"Returns the maximum length of the stringified elements of the matrix"
[m]
(apply max (map max-str-len m)))
; could do latex, but what's the point
(defn pretty-matrix-rows
"Make a list of fancy display strings of the rows given matrix"
([m] (pretty-matrix-rows m false))
([m add-bar]
(let [max-len (max-str-len-matrix m)
width (matrix-width m)
row-len (+ (- width 1) (* max-len width))
middle (map #(draw-row % max-len add-bar "┃ " " ┃") m)
space (apply str (repeat (+ (if add-bar 2 0) row-len) " "))]
(concat [(str "┏ " space " ┓")]
middle
[(str "┗ " space " ┛")]))))
(defn pretty-matrix
"Make a fancy display string from the given matrix"
([m] (pretty-matrix m false))
([m add-bar]
(str/join "\n" (pretty-matrix-rows m add-bar))))
(defn pprint-matrix
"Pretty print the given matrix"
([m] (pprint-matrix m false))
([m add-bar]
((comp println #(pretty-matrix % add-bar)) m)
m))
;; repl commands
(defn fmt-num
"Format a number, empty string for 1"
[n]
(if (= 1 n)
""
(str n)))
(defn mid-sign
"Format a term as either '+ n' or '- n'"
[n]
(if (pos? n)
(str "+ " (fmt-num n))
(str "- " (fmt-num (* -1 n)))))
(defn single-term
[val name first]
(if (zero? val)
""
(str (if first
(fmt-num val)
(mid-sign val))
name)))
; could be generalized
(defn equation-from-row
"Write a pretty equation from one row of an augmented matrix"
([row] (apply equation-from-row row))
([x eq] (str (single-term x "x" true)
" = "
eq))
([x y eq]
(let [x (single-term x "x" true)
y (single-term y "y" (empty? x))]
(str x
(if (not-empty x) " ")
y
(if (not-empty y) " ")
"= " eq)))
([x y z eq]
(let [x (single-term x "x" true)
y (single-term y "y" (empty? x))
z (single-term z "z" (and (empty? x) (empty? y)))]
(str x
(if (not-empty x) " ")
y
(if (not-empty y) " ")
z
(if (not-empty z) " ")
"= " eq))))
(defn print-matrix-equations
[m]
(do
(last (for [row m]
(println (equation-from-row row))))
m))
(defn swap [n m] #(swap-rows % n m))
(defn add [from to] #(add-rows % from to))
(defn add-mul [n from to] #(add-rows-with-mul % n from to))
(defn mul [n at] #(mul-row % n at))
(def print-equations print-matrix-equations)
(defn apply-input
[m]
(let [got (read)]
(case got
[:q
:wq
:exit
:x
:done] m
((eval got) m))))
(defn matrix-repl
([m] (matrix-repl m false))
([m add-bar]
(pprint-matrix m add-bar)
(iterate (comp #(pprint-matrix % add-bar) apply-input) m)))
(defn -main
"I don't do a whole lot ... yet."
[& args]
@ -229,6 +17,7 @@
; TODO: parse equations (easy, but requires context (e.g. x = 1 could be any number of variables))
; TODO: undoing
; will have to have a higher level thing for storing state
; use a set for options on the higher-level thing #{:show-equations? :show-bar? :color?}
; TODO: solving id-matrix
; TODO: solving gaussian-eliminated matrix
; TODO: automation

View file

@ -0,0 +1,33 @@
(ns matrix.interactive
(:require [matrix.base :refer :all])
(:require [matrix.render :refer :all]))
(defn print-matrix-equations
[m]
(do
(last (for [row m]
(println (equation-from-row row))))
m))
(defn swap [n m] #(swap-rows % n m))
(defn add [from to] #(add-rows % from to))
(defn add-mul [n from to] #(add-rows-with-mul % n from to))
(defn mul [n at] #(mul-row % n at))
(def print-equations print-matrix-equations)
(defn apply-input
[m]
(let [got (read)]
(case got
[:q
:wq
:exit
:x
:done] m
((eval got) m))))
(defn matrix-repl
([m] (matrix-repl m false))
([m add-bar]
(pprint-matrix m add-bar)
(iterate (comp #(pprint-matrix % add-bar) apply-input) m)))

119
src/matrix/render.clj Normal file
View file

@ -0,0 +1,119 @@
(ns matrix.render
(:require [clojure.string :as str])
(:require [matrix.base :refer :all]))
(defn surround-with
"Surround a string with the given left and right"
([left right] #(surround-with left right %))
([left right s] (str left s right)))
(defn add-before-last
[what where]
(concat (take (dec (count where)) where)
[what]
[(last where)]))
(defn draw-row
"Takes a seq of strs and a max length and draws a row.
Optionally takes a left and right to surround with"
([seq max-len add-bar]
(let [fmt-str (str "%" max-len "s")]
(->> seq
(map (partial format fmt-str))
((if add-bar
(partial add-before-last "│")
identity))
(str/join " "))))
([seq max-len add-bar left right]
((surround-with left right) (draw-row seq max-len add-bar))))
(defn max-str-len
"Returns the maximum length of a stringified seq"
[seq]
(->> seq
(map str)
(map count)
(apply max 0)))
(defn max-str-len-matrix
"Returns the maximum length of the stringified elements of the matrix"
[m]
(apply max (map max-str-len m)))
; could do latex, but what's the point
(defn pretty-matrix-rows
"Make a list of fancy display strings of the rows given matrix"
([m] (pretty-matrix-rows m false))
([m add-bar]
(let [max-len (max-str-len-matrix m)
width (matrix-width m)
row-len (+ (- width 1) (* max-len width))
middle (map #(draw-row % max-len add-bar "┃ " " ┃") m)
space (apply str (repeat (+ (if add-bar 2 0) row-len) " "))]
(concat [(str "┏ " space " ┓")]
middle
[(str "┗ " space " ┛")]))))
(defn pretty-matrix
"Make a fancy display string from the given matrix"
([m] (pretty-matrix m false))
([m add-bar]
(str/join "\n" (pretty-matrix-rows m add-bar))))
(defn pprint-matrix
"Pretty print the given matrix"
([m] (pprint-matrix m false))
([m add-bar]
((comp println #(pretty-matrix % add-bar)) m)
m))
;; repl commands
(defn fmt-num
"Format a number, empty string for 1"
[n]
(if (= 1 n)
""
(str n)))
(defn mid-sign
"Format a term as either '+ n' or '- n'"
[n]
(if (pos? n)
(str "+ " (fmt-num n))
(str "- " (fmt-num (* -1 n)))))
(defn single-term
[val name first]
(if (zero? val)
""
(str (if first
(fmt-num val)
(mid-sign val))
name)))
; could be generalized
(defn equation-from-row
"Write a pretty equation from one row of an augmented matrix"
([row] (apply equation-from-row row))
([x eq] (str (single-term x "x" true)
" = "
eq))
([x y eq]
(let [x (single-term x "x" true)
y (single-term y "y" (empty? x))]
(str x
(if (not-empty x) " ")
y
(if (not-empty y) " ")
"= " eq)))
([x y z eq]
(let [x (single-term x "x" true)
y (single-term y "y" (empty? x))
z (single-term z "z" (and (empty? x) (empty? y)))]
(str x
(if (not-empty x) " ")
y
(if (not-empty y) " ")
z
(if (not-empty z) " ")
"= " eq))))