pixelfaucet/src/matrix.cr
2022-01-11 21:48:23 -05:00

124 lines
2.8 KiB
Crystal

module PF
struct Matrix(T, W, H)
property values = Slice(T).new(W * H, T.new(0))
# Creates a new square `Matrix` with the given *args*
#
# ```
# m = Matrix[1, 2, 3, 4]
# m[0, 0] # => 1
# m[1, 0] # => 2
# m[0, 1] # => 3
# m[1, 1] # => 4
# m.class # => Matrix(Int32, 2, 2)
# ```
macro [](*args)
%values = Slice(typeof({{*args}})).new({{args.size}}, typeof({{*args}}).new(0))
{% for arg, i in args %}
%values.to_unsafe[{{i}}] = {{arg}}
{% end %}
# width and height are the isqrt of args.size
{% if args.size == 1 %}
PF::Matrix(typeof({{*args}}), 1, 1).new(%values)
{% elsif args.size == 4 %}
PF::Matrix(typeof({{*args}}), 2, 2).new(%values)
{% elsif args.size == 9 %}
PF::Matrix(typeof({{*args}}), 3, 3).new(%values)
{% elsif args.size == 16 %}
PF::Matrix(typeof({{*args}}), 4, 4).new(%values)
{% else %}
raise "Cannot determine width and height of matrix with {{ args.size }} elements, " \
"please provide them explicitly Matrix(Int32, 4, 4).new(...)"
{% end %}
end
def self.identity
new.tap do |m|
m.size.times { |n| m[n, n] = T.new(1) }
end
end
def initialize
end
def initialize(@values)
end
# Create a new matrix
#
# ```
# PF::Matrix(Int32, 2, 2).new(1, 2, 3, 4)
# ```
def initialize(*nums : T)
nums.each_with_index { |n, i| @values.to_unsafe[i] = n }
end
# Width of the matrix
def width
W
end
# Height of the matrix
def height
H
end
def size
{% if W == H %}
W
{% else %}
raise "Matrix({{W}}x{{H}}) is not square"
{% end %}
end
# Tests the equality of two matricies
def ==(other : Matrix)
self.values == other.values
end
# Get the index of an element in the matrix by *x* and *y* coordinates
def index(x : Int, y : Int)
y * width + x
end
def [](i : Int)
@values[i]
end
def []=(i : Int, value : T)
@values[i] = value
end
# Get an element
def [](x : Int, y : Int)
self[index(x, y)]
end
# Set an element at an *x* and *y* position
def []=(x : Int, y : Int, value : T)
self[index(x, y)] = value
end
def *(other : Matrix)
result = Matrix(T, W, H).new
{% for y in (0...H) %}
{% for x in (0...W) %}
{% for n in (0...W) %}
result[{{x}},{{y}}] = result[{{x}},{{y}}] + self[{{n}}, {{y}}] * other[{{x}}, {{n}}]
{% end %}
{% end %}
{% end %}
result
end
def inspect
String.build do |io|
H.times do |h|
io << '['
W.times { |w| io << self[w, h] }
io << "]\n"
end
end
end
end
end