This documentation is automatically generated by online-judge-tools/verification-helper

:warning: spec/point/point_spec.cr

Depends on

Code

require "spec"
require "../../src/point"

private macro check_direction(name, dy, dx)
  it ".{{name}}" do
    Point.{{name}}.should eq Point.new({{dy}}, {{dx}})
  end

  it "#" + "{{name}}" do
    Point.new(1, 1).{{name}}.should eq Point.new(1 + {{dy}}, 1 + {{dx}})
  end
end

private macro check_binary_operator(op)
  it "\#{{op.id}}" do
    a, b = Point.new(1, 2), Point.new(3, 4)
    (a {{op.id}} b).should eq Point.new(1 {{op.id}} 3, 2 {{op.id}} 4)
    (a {{op.id}} 5).should eq Point.new(1 {{op.id}} 5, 2 {{op.id}} 5)
  end
end

private H = 3
private W = 4

describe Point do
  it ".set_range and .height and .width" do
    Point.reset_range
    expect_raises(NilAssertionError) { Point.height }
    expect_raises(NilAssertionError) { Point.width }
    Point.height?.should be_nil
    Point.width?.should be_nil
    Point.set_range(H, W)
    Point.height.should eq H
    Point.width.should eq W
    Point.height?.should eq H
    Point.width?.should eq W
  end

  it ".size" do
    Point.size.should eq H*W
  end

  it "extend Indexable(Point)" do
    array = (0...H).to_a.product((0...W).to_a).map { |y, x| Point.new(y, x) }
    Point.to_a.should eq array
    Point[0].should eq Point.new(0, 0)
    expect_raises(IndexError) { Point[H*W] }
  end

  it ".new" do
    Point.new.should eq Point.new(0, 0)
    Point.new(H, W).should eq Point.new(H, W)
  end

  it ".from" do
    Point.from([0, 1]).should eq Point.new(0, 1)
    expect_raises(ArgumentError) { Point.from [0] }
    expect_raises(ArgumentError) { Point.from [0, 1, 2] }
  end

  it ".[](y, x)" do
    Point[1, 1].should eq Point.new(1, 1)
  end

  check_direction(zero, 0, 0)
  check_direction(up, -1, 0)
  check_direction(left, 0, -1)
  check_direction(down, 1, 0)
  check_direction(right, 0, 1)
  check_direction(ul, -1, -1)
  check_direction(ur, -1, 1)
  check_direction(dl, 1, -1)
  check_direction(dr, 1, 1)

  check_binary_operator("+")
  check_binary_operator("-")
  check_binary_operator("*")
  check_binary_operator("//")
  check_binary_operator("%")

  it "#xy" do
    Point.new(1, 2).xy.should eq Point.new(2, 1)
  end

  it "#yx" do
    Point.new(1, 2).yx.should eq Point.new(1, 2)
  end

  it "#dot" do
    Point[1, 2].dot(Point[2, 3]).should eq 8
  end

  it "#cross" do
    Point[1, 2].cross(Point[2, 3]).should eq 1
  end

  it "#rotate90" do
    Point[0, 0].rotate90.should eq Point[0, 2]
    Point[0, 1].rotate90.should eq Point[1, 2]
    Point[1, 0].rotate90.should eq Point[0, 1]
    Point[1, 1].rotate90.should eq Point[1, 1]
    x = Point[1, 2]
    x.rotate90!.should eq Point[2, 1]
    x.should eq Point[2, 1]
  end

  it "#flip_vertically" do
    Point[0, 0].flip_vertically.should eq Point[2, 0]
    Point[0, 1].flip_vertically.should eq Point[2, 1]
    Point[1, 0].flip_vertically.should eq Point[1, 0]
    Point[1, 1].flip_vertically.should eq Point[1, 1]
    x = Point[0, 0]
    x.flip_vertically!.should eq Point[2, 0]
    x.should eq Point[2, 0]
  end

  it "#flip_horizontally" do
    Point[0, 0].flip_horizontally.should eq Point[0, 3]
    Point[0, 1].flip_horizontally.should eq Point[0, 2]
    Point[1, 0].flip_horizontally.should eq Point[1, 3]
    Point[1, 1].flip_horizontally.should eq Point[1, 2]
    x = Point[0, 0]
    x.flip_horizontally!.should eq Point[0, 3]
    x.should eq Point[0, 3]
  end

  it "#==(other)" do
    Point.each do |p1|
      Point.each do |p2|
        (p1 == p2).should eq({p1.y, p1.x} == {p2.y, p2.x})
      end
    end
  end

  it "#<=>(other)" do
    Point.each do |p1|
      Point.each do |p2|
        (p1 <=> p2).should eq({p1.y, p1.x} <=> {p2.y, p2.x})
      end
    end
  end

  it "include Comparable(Point)" do
    Point.new(2, 2).clamp(Point.new(0, 0), Point.new(1, 1)).should eq Point.new(1, 1)
  end

  it "#[]" do
    a = Point.new(1, 2)
    a[0].should eq 1
    a[1].should eq 2
    expect_raises(IndexError) { a[-1] }
    expect_raises(IndexError) { a[2] }
    x, y = a
    [x, y].should eq [1, 2]
  end

  it "#succ" do
    Point.new(1, 2).succ.should eq Point.new(1, 3)
    Point.new(1, W - 1).succ.should eq Point.new(2, 0)
    expect_raises(IndexError) { Point.new(H - 1, W - 1).succ }
    expect_raises(IndexError) { Point.new(0, -1).succ }
    expect_raises(IndexError) { Point.new(H, 0).succ }
  end

  it "#pred" do
    Point.new(1, 2).pred.should eq Point.new(1, 1)
    Point.new(1, 0).pred.should eq Point.new(0, W - 1)
    expect_raises(IndexError) { Point.new(0, 0).pred }
    expect_raises(IndexError) { Point.new(0, -1).pred }
    expect_raises(IndexError) { Point.new(H, 0).pred }
  end

  it "#in_range?" do
    (-2..H + 2).each do |y|
      (-2..W + 2).each do |x|
        flag = (0...H).includes?(y) && (0...W).includes?(x)
        Point.new(y, x).in_range?.should eq flag
      end
    end
  end

  it "#to_i" do
    i = 0
    Point.each do |p|
      p.to_i.should eq i
      i += 1
    end
    expect_raises(IndexError) { Point.new(-1, 0).to_i }
    expect_raises(IndexError) { Point.new(H, 0).to_i }
  end

  it "#distance_square" do
    Point.new(3, 4).distance_square(Point.new(7, 9)).should eq 41
    Point.new(3, 4).distance_square(Point.new(3, 0)).should eq 16
    Point.new(3, 4).distance_square(Point.new(3, 4)).should eq 0
  end

  it "#distance" do
    Point.new(3, 4).distance(Point.new(7, 9)).should eq Math.sqrt(41)
    Point.new(3, 4).distance(Point.new(3, 0)).should eq 4.0
    Point.new(3, 4).distance(Point.new(3, 4)).should eq 0
  end

  it "#manhattan" do
    Point.new(3, 4).manhattan(Point.new(7, 9)).should eq 9
    Point.new(3, 4).manhattan(Point.new(3, 0)).should eq 4
    Point.new(3, 4).manhattan(Point.new(3, 4)).should eq 0
  end

  it "#chebyshev" do
    Point.new(3, 4).manhattan(Point.new(7, 9)).should eq 9
    Point.new(3, 4).manhattan(Point.new(3, 0)).should eq 4
    Point.new(3, 4).manhattan(Point.new(3, 4)).should eq 0
  end

  it "#adjacent4" do
    a = Point.new(3, 4)
    a.adjacent4.should be_a(Iterator(Point))
    expect = [a.up, a.left, a.down, a.right]
    a.adjacent4.to_a.should eq expect

    res = [] of Point
    a.adjacent4 { |p| res << p }
    res.should eq expect
  end

  it "#adj4_in_range" do
    Point.new.adj4_in_range.should be_a(Iterator(Point))
    a = Point.new(H - 1, W - 1)
    a.adj4_in_range.to_a.should eq [a.up, a.left]
    a = Point.new(0, 0)
    a.adj4_in_range.to_a.should eq [a.down, a.right]

    res = [] of Point
    a.adj4_in_range { |p| res << p }
    res.should eq [a.down, a.right]
  end

  it "#adjacent8" do
    a = Point.new(3, 4)
    a.adjacent8.should be_a(Iterator(Point))
    expect = [a.up, a.left, a.down, a.right, a.ul, a.ur, a.dl, a.dr]
    a.adjacent8.to_a.should eq expect

    res = [] of Point
    a.adjacent8 { |p| res << p }
    res.should eq expect
  end

  it "#adj8_in_range" do
    Point.new.adj8_in_range.should be_a(Iterator(Point))
    a = Point.new(H - 1, W - 1)
    a.adj8_in_range.to_a.should eq [a.up, a.left, a.ul]
    a = Point.new(0, 0)
    a.adj8_in_range.to_a.should eq [a.down, a.right, a.dr]

    res = [] of Point
    a.adj8_in_range { |p| res << p }
    res.should eq [a.down, a.right, a.dr]
  end

  it "#to_s" do
    Point.new(3, 4).to_s.should eq "(3, 4)"
  end

  it "#inspect" do
    Point.new(3, 4).inspect.should eq "(3, 4)"
  end

  it "to_direction_char?" do
    [
      {Point.left * 1, 'L'}, {Point.right * 1, 'R'}, {Point.up * 1, 'U'}, {Point.down * 1, 'D'},
      {Point.left * 9, 'L'}, {Point.right * 9, 'R'}, {Point.up * 9, 'U'}, {Point.down * 9, 'D'},
      {Point.dr, nil},
    ].each do |p, expected|
      p.to_direction_char?.should eq expected
    end

    {"<>^v", "1234"}.each do |str|
      [Point.left, Point.right, Point.up, Point.down].zip(str.chars) do |p, c|
        p.to_direction_char?(str).should eq c
      end
    end
  end

  it ".to_direction?(c : Char)" do
    Point.to_direction?('L').should eq Point.left
    Point.to_direction?('R').should eq Point.right
    Point.to_direction?('U').should eq Point.up
    Point.to_direction?('D').should eq Point.down
    Point.to_direction?('?').should be_nil
  end

  it ".to_direction?(s : String)" do
    [
      {"L", Point.left}, {"R", Point.right}, {"U", Point.up}, {"D", Point.down},
      {"LU", Point.ul}, {"UL", Point.ul}, {"LD", Point.dl}, {"DL", Point.dl},
      {"RU", Point.ur}, {"UR", Point.ur}, {"RD", Point.dr}, {"DR", Point.dr},
      {"?", nil}, {"LL", nil}, {"LR", nil}, {"UD", nil}, {"L?", nil}, {"", nil}, {"LRU", nil},
    ].each do |str, expected|
      Point.to_direction?(str).should eq expected
    end
  end
end

describe Indexable do
  it "#[](point : Point)" do
    a = [[0, 1], [2, 3], [4]]
    [{0, 0, 0}, {0, 1, 1}, {1, 0, 2}, {1, 1, 3}, {2, 0, 4}].each do |y, x, value|
      a[Point.new(y, x)].should eq value
    end
    [{2, 1}, {0, 2}, {0, -1}, {-1, 0}, {3, 0}].each do |y, x|
      expect_raises(IndexError) { a[Point.new(y, x)] }
    end

    b = %w[ab cd e]
    [{0, 0, 'a'}, {0, 1, 'b'}, {1, 0, 'c'}, {1, 1, 'd'}, {2, 0, 'e'}].each do |y, x, char|
      b[Point.new(y, x)].should eq char
    end
    [{2, 1}, {0, 2}, {0, -1}, {-1, 0}, {3, 0}].each do |y, x|
      expect_raises(IndexError) { b[Point.new(y, x)] }
    end
  end

  it "#[]?(point : Point)" do
    a = [[0, 1], [2, 3], [4]]
    [
      {0, 0, 0}, {0, 1, 1}, {1, 0, 2}, {1, 1, 3}, {2, 0, 4},
      {0, 2, nil}, {0, -1, nil}, {-1, 0, nil}, {3, 0, nil},
    ].each do |y, x, value|
      a[Point.new(y, x)]?.should eq value
    end

    b = %w[ab cd e]
    [
      {0, 0, 'a'}, {0, 1, 'b'}, {1, 0, 'c'}, {1, 1, 'd'}, {2, 0, 'e'},
      {2, 1, nil}, {0, 2, nil}, {0, -1, nil}, {-1, 0, nil}, {3, 0, nil},
    ].each do |y, x, value|
      b[Point.new(y, x)]?.should eq value
    end
  end
end

describe Array do
  it "#[]=(point : Point, value)" do
    a = [[0, 1], [2]]
    a[Point.new(0, 0)] = 3
    a[Point.new(0, 1)] = 4
    a[Point.new(1, 0)] = 5
    expect_raises(IndexError) { a[Point.new(2, 1)] = 0 }
    expect_raises(IndexError) { a[Point.new(0, 2)] = 0 }
    expect_raises(IndexError) { a[Point.new(0, -1)] = 0 }
    expect_raises(IndexError) { a[Point.new(-1, 0)] = 0 }
    expect_raises(IndexError) { a[Point.new(3, 0)] = 0 }
    a.should eq [[3, 4], [5]]
  end
end
require "spec"

# require "../../src/point"
struct Point
  include Comparable(Point)
  extend Indexable(Point)

  property y : Int32, x : Int32

  Direction4 = [Point.up, Point.left, Point.down, Point.right]
  Direction8 = Direction4 + [Point.ul, Point.ur, Point.dl, Point.dr]

  class_getter! height : Int32, width : Int32

  def self.set_range(height : Int, width : Int)
    raise ArgumentError.new unless 0 < height && 0 < width
    @@height, @@width = height, width
  end

  def self.reset_range
    @@height, @@width = nil, nil
  end

  def self.size
    height * width
  end

  def self.unsafe_fetch(index : Int)
    Point.new(index // Point.width, index % Point.width)
  end

  def self.each(h : Int, w : Int, &block)
    h.times do |y|
      w.times do |x|
        yield Point[y, x]
      end
    end
  end

  def self.each(y : Int, w : Int)
    size.times.map { |i| Point.new(i) }
  end

  def initialize
    @y, @x = 0, 0
  end

  def initialize(y : Int, x : Int)
    @y, @x = y.to_i, x.to_i
  end

  def initialize(i : Int)
    raise ArgumentError.new unless 0 <= i && i < Point.size
    @y, @x = i // Point.width, i % Point.width
  end

  # Creates point fomr given array.
  def self.from(array : Array) : self
    raise ArgumentError.new unless array.size == 2
    Point.new(array.unsafe_fetch(0), array.unsafe_fetch(1))
  end

  # Alias for `.new(y : Int, x : Int)`
  def self.[](y : Int, x : Int) : self
    Point.new(y, x)
  end

  def self.scan(scanner, io : IO) : self
    Point.new(scanner.i(io), scanner.i(io))
  end

  {% for name, d in {
                      :zero => {0, 0},
                      :up => {-1, 0}, :down => {1, 0}, :left => {0, -1}, :right => {0, 1},
                      :ul => {-1, -1}, :ur => {-1, 1}, :dl => {1, -1}, :dr => {1, 1},
                    } %}
    {% dy = d[0]; dx = d[1] %}

    # Returns `Point.new({{dy}}, {{dx}})`
    def self.{{name.id}}
      Point.new({{dy}}, {{dx}})
    end

    # Returns `self + Point.new({{dy}}, {{dx}})`
    def {{name.id}}
      Point.new(y + {{dy}}, x + {{dx}})
    end

    # Adds `Point.new({{dy}}, {{dx}})`
    def {{name.id}}!
      @y += {{dy}}
      @x += {{dx}}
      self
    end
  {% end %}

  {% for op in %w[+ - * // %] %}
    def {{op.id}}(other : Point)
      Point.new(y {{op.id}} other.y, x {{op.id}} other.x)
    end

    def {{op.id}}(other : Int)
      Point.new(y {{op.id}} other, x {{op.id}} other)
    end
  {% end %}

  # Returns `Point.new(x, y)`
  def xy
    Point.new(x, y)
  end

  # Returns `Point.new(y, x)`
  def yx
    self
  end

  def dot(other : Point)
    x * other.x + y * other.y
  end

  def cross(other : Point)
    x * other.y - y * other.x
  end

  # Rotates 90 degrees clockwise.
  #
  # x....      ..x
  # .....  ->  ...
  # ....y      ...
  #            ...
  #            y..
  #
  def rotate90!
    @y, @x = x, Point.height - 1 - y
    self
  end

  # :ditto:
  def rotate90
    dup.rotate90!
  end

  # Flips the grid vertically.
  #
  # .x....      .....y
  # ......  ->  ......
  # .....y      .x....
  #
  def flip_vertically!
    @y, @x = Point.height - y - 1, x
    self
  end

  # :ditto:
  def flip_vertically
    dup.flip_vertically!
  end

  # Flips the grid horizontally.
  #
  # .x....      ....x.
  # ......  ->  ......
  # .....y      y.....
  #
  def flip_horizontally!
    @y, @x = y, Point.width - x - 1
    self
  end

  # :ditto:
  def flip_horizontally
    dup.flip_horizontally!
  end

  def ==(other : Point)
    x == other.x && y == other.y
  end

  def <=>(other : Point)
    {y, x} <=> {other.y, other.x}
  end

  def [](i : Int)
    return y if i == 0
    return x if i == 1
    raise IndexError.new
  end

  def succ
    raise IndexError.new unless in_range? && self != Point.last
    if x < Point.width - 1
      Point.new(y, x + 1)
    else
      Point.new(y + 1, 0)
    end
  end

  def pred
    raise IndexError.new unless in_range? && self != Point.first
    if x > 0
      Point.new(y, x - 1)
    else
      Point.new(y - 1, Point.width - 1)
    end
  end

  def in_range?
    (0...Point.height).includes?(y) && (0...Point.width).includes?(x)
  end

  def to_i
    raise IndexError.new unless in_range?
    y * Point.width + x
  end

  def distance_square(other : Point)
    (y - other.y) ** 2 + (x - other.x) ** 2
  end

  def distance(other : Point)
    Math.sqrt(distance_square(other))
  end

  def manhattan(other : Point)
    (y - other.y).abs + (x - other.x).abs
  end

  def chebyshev(other : Point)
    Math.max((y - other.y).abs, (x - other.x).abs)
  end

  {% for i in [4, 8] %}
    def adjacent{{i}}(&block) : Nil
      Direction{{i}}.each do |d|
        yield self + d
      end
    end

    def adjacent{{i}}
      Direction{{i}}.each.map { |p| self + p }
    end

    def adj{{i}}_in_range(&block) : Nil
      Direction{{i}}.each do |d|
        point = self + d
        yield point if point.in_range?
      end
    end

    def adj{{i}}_in_range
      adjacent{{i}}.select(&.in_range?)
    end
  {% end %}

  # Writes a string representation of the point to *io*.
  #
  # ```
  # Point.new(1, 2).to_s # => "(1, 2)"
  # ```
  def to_s(io : IO) : Nil
    io << '(' << y << ", " << x << ')'
  end

  # Writes a string representation of the point to *io*.
  #
  # ```
  # Point.new(1, 2).inspect # => "(1, 2)"
  # ```
  def inspect(io : IO) : Nil
    to_s(io)
  end

  # Convert `Point` into `Char` representing direction.
  #
  # ```
  # Point.down.to_direction_char? # => 'D'
  # Point.left.to_direction_char? # => 'L'
  # ```
  def to_direction_char?(lrud = "LRUD") : Char?
    if y == 0 && x != 0
      x < 0 ? lrud[0] : lrud[1]
    elsif x == 0 && y != 0
      y < 0 ? lrud[2] : lrud[3]
    end
  end

  # Convert `Char` representing direction into `Point`.
  #
  # ```
  # Point.to_direction?('R') # => Point.new(0, 1)
  # ```
  def self.to_direction?(c : Char, lrud = "LRUD")
    raise ArgumentError.new unless lrud.size == 4
    lrud.index(c).try { |i| {left, right, up, down}[i] }
  end

  # Convert `String` representing direction into `Point`.
  #
  # ```
  # Point.to_direction?("DR") # => Point.new(1, 1)
  # ```
  def self.to_direction?(s : String, lrud = "LRUD")
    case s.size
    when 1
      to_direction?(s[0], lrud)
    when 2
      p1 = to_direction?(s[0], lrud) || return nil
      p2 = to_direction?(s[1], lrud) || return nil
      return nil unless p1.x ^ p2.x != 0 && p1.y ^ p2.y != 0
      p1 + p2
    end
  end
end

module Indexable(T)
  private def check_index_out_of_bounds(point : Point)
    check_index_out_of_bounds(point) { raise IndexError.new }
  end

  private def check_index_out_of_bounds(point : Point)
    if 0 <= point.y < size && 0 <= point.x < unsafe_fetch(point.y).size
      point
    else
      yield
    end
  end

  def fetch(point : Point)
    point = check_index_out_of_bounds(point) do
      return yield point
    end
    unsafe_fetch(point.y)[point.x]
  end

  def [](point : Point)
    fetch(point) { raise IndexError.new }
  end

  def []?(point : Point)
    fetch(point, nil)
  end
end

class Array(T)
  def []=(point : Point, value)
    index = check_index_out_of_bounds point
    @buffer[index.y][index.x] = value
  end
end

private macro check_direction(name, dy, dx)
  it ".{{name}}" do
    Point.{{name}}.should eq Point.new({{dy}}, {{dx}})
  end

  it "#" + "{{name}}" do
    Point.new(1, 1).{{name}}.should eq Point.new(1 + {{dy}}, 1 + {{dx}})
  end
end

private macro check_binary_operator(op)
  it "\#{{op.id}}" do
    a, b = Point.new(1, 2), Point.new(3, 4)
    (a {{op.id}} b).should eq Point.new(1 {{op.id}} 3, 2 {{op.id}} 4)
    (a {{op.id}} 5).should eq Point.new(1 {{op.id}} 5, 2 {{op.id}} 5)
  end
end

private H = 3
private W = 4

describe Point do
  it ".set_range and .height and .width" do
    Point.reset_range
    expect_raises(NilAssertionError) { Point.height }
    expect_raises(NilAssertionError) { Point.width }
    Point.height?.should be_nil
    Point.width?.should be_nil
    Point.set_range(H, W)
    Point.height.should eq H
    Point.width.should eq W
    Point.height?.should eq H
    Point.width?.should eq W
  end

  it ".size" do
    Point.size.should eq H*W
  end

  it "extend Indexable(Point)" do
    array = (0...H).to_a.product((0...W).to_a).map { |y, x| Point.new(y, x) }
    Point.to_a.should eq array
    Point[0].should eq Point.new(0, 0)
    expect_raises(IndexError) { Point[H*W] }
  end

  it ".new" do
    Point.new.should eq Point.new(0, 0)
    Point.new(H, W).should eq Point.new(H, W)
  end

  it ".from" do
    Point.from([0, 1]).should eq Point.new(0, 1)
    expect_raises(ArgumentError) { Point.from [0] }
    expect_raises(ArgumentError) { Point.from [0, 1, 2] }
  end

  it ".[](y, x)" do
    Point[1, 1].should eq Point.new(1, 1)
  end

  check_direction(zero, 0, 0)
  check_direction(up, -1, 0)
  check_direction(left, 0, -1)
  check_direction(down, 1, 0)
  check_direction(right, 0, 1)
  check_direction(ul, -1, -1)
  check_direction(ur, -1, 1)
  check_direction(dl, 1, -1)
  check_direction(dr, 1, 1)

  check_binary_operator("+")
  check_binary_operator("-")
  check_binary_operator("*")
  check_binary_operator("//")
  check_binary_operator("%")

  it "#xy" do
    Point.new(1, 2).xy.should eq Point.new(2, 1)
  end

  it "#yx" do
    Point.new(1, 2).yx.should eq Point.new(1, 2)
  end

  it "#dot" do
    Point[1, 2].dot(Point[2, 3]).should eq 8
  end

  it "#cross" do
    Point[1, 2].cross(Point[2, 3]).should eq 1
  end

  it "#rotate90" do
    Point[0, 0].rotate90.should eq Point[0, 2]
    Point[0, 1].rotate90.should eq Point[1, 2]
    Point[1, 0].rotate90.should eq Point[0, 1]
    Point[1, 1].rotate90.should eq Point[1, 1]
    x = Point[1, 2]
    x.rotate90!.should eq Point[2, 1]
    x.should eq Point[2, 1]
  end

  it "#flip_vertically" do
    Point[0, 0].flip_vertically.should eq Point[2, 0]
    Point[0, 1].flip_vertically.should eq Point[2, 1]
    Point[1, 0].flip_vertically.should eq Point[1, 0]
    Point[1, 1].flip_vertically.should eq Point[1, 1]
    x = Point[0, 0]
    x.flip_vertically!.should eq Point[2, 0]
    x.should eq Point[2, 0]
  end

  it "#flip_horizontally" do
    Point[0, 0].flip_horizontally.should eq Point[0, 3]
    Point[0, 1].flip_horizontally.should eq Point[0, 2]
    Point[1, 0].flip_horizontally.should eq Point[1, 3]
    Point[1, 1].flip_horizontally.should eq Point[1, 2]
    x = Point[0, 0]
    x.flip_horizontally!.should eq Point[0, 3]
    x.should eq Point[0, 3]
  end

  it "#==(other)" do
    Point.each do |p1|
      Point.each do |p2|
        (p1 == p2).should eq({p1.y, p1.x} == {p2.y, p2.x})
      end
    end
  end

  it "#<=>(other)" do
    Point.each do |p1|
      Point.each do |p2|
        (p1 <=> p2).should eq({p1.y, p1.x} <=> {p2.y, p2.x})
      end
    end
  end

  it "include Comparable(Point)" do
    Point.new(2, 2).clamp(Point.new(0, 0), Point.new(1, 1)).should eq Point.new(1, 1)
  end

  it "#[]" do
    a = Point.new(1, 2)
    a[0].should eq 1
    a[1].should eq 2
    expect_raises(IndexError) { a[-1] }
    expect_raises(IndexError) { a[2] }
    x, y = a
    [x, y].should eq [1, 2]
  end

  it "#succ" do
    Point.new(1, 2).succ.should eq Point.new(1, 3)
    Point.new(1, W - 1).succ.should eq Point.new(2, 0)
    expect_raises(IndexError) { Point.new(H - 1, W - 1).succ }
    expect_raises(IndexError) { Point.new(0, -1).succ }
    expect_raises(IndexError) { Point.new(H, 0).succ }
  end

  it "#pred" do
    Point.new(1, 2).pred.should eq Point.new(1, 1)
    Point.new(1, 0).pred.should eq Point.new(0, W - 1)
    expect_raises(IndexError) { Point.new(0, 0).pred }
    expect_raises(IndexError) { Point.new(0, -1).pred }
    expect_raises(IndexError) { Point.new(H, 0).pred }
  end

  it "#in_range?" do
    (-2..H + 2).each do |y|
      (-2..W + 2).each do |x|
        flag = (0...H).includes?(y) && (0...W).includes?(x)
        Point.new(y, x).in_range?.should eq flag
      end
    end
  end

  it "#to_i" do
    i = 0
    Point.each do |p|
      p.to_i.should eq i
      i += 1
    end
    expect_raises(IndexError) { Point.new(-1, 0).to_i }
    expect_raises(IndexError) { Point.new(H, 0).to_i }
  end

  it "#distance_square" do
    Point.new(3, 4).distance_square(Point.new(7, 9)).should eq 41
    Point.new(3, 4).distance_square(Point.new(3, 0)).should eq 16
    Point.new(3, 4).distance_square(Point.new(3, 4)).should eq 0
  end

  it "#distance" do
    Point.new(3, 4).distance(Point.new(7, 9)).should eq Math.sqrt(41)
    Point.new(3, 4).distance(Point.new(3, 0)).should eq 4.0
    Point.new(3, 4).distance(Point.new(3, 4)).should eq 0
  end

  it "#manhattan" do
    Point.new(3, 4).manhattan(Point.new(7, 9)).should eq 9
    Point.new(3, 4).manhattan(Point.new(3, 0)).should eq 4
    Point.new(3, 4).manhattan(Point.new(3, 4)).should eq 0
  end

  it "#chebyshev" do
    Point.new(3, 4).manhattan(Point.new(7, 9)).should eq 9
    Point.new(3, 4).manhattan(Point.new(3, 0)).should eq 4
    Point.new(3, 4).manhattan(Point.new(3, 4)).should eq 0
  end

  it "#adjacent4" do
    a = Point.new(3, 4)
    a.adjacent4.should be_a(Iterator(Point))
    expect = [a.up, a.left, a.down, a.right]
    a.adjacent4.to_a.should eq expect

    res = [] of Point
    a.adjacent4 { |p| res << p }
    res.should eq expect
  end

  it "#adj4_in_range" do
    Point.new.adj4_in_range.should be_a(Iterator(Point))
    a = Point.new(H - 1, W - 1)
    a.adj4_in_range.to_a.should eq [a.up, a.left]
    a = Point.new(0, 0)
    a.adj4_in_range.to_a.should eq [a.down, a.right]

    res = [] of Point
    a.adj4_in_range { |p| res << p }
    res.should eq [a.down, a.right]
  end

  it "#adjacent8" do
    a = Point.new(3, 4)
    a.adjacent8.should be_a(Iterator(Point))
    expect = [a.up, a.left, a.down, a.right, a.ul, a.ur, a.dl, a.dr]
    a.adjacent8.to_a.should eq expect

    res = [] of Point
    a.adjacent8 { |p| res << p }
    res.should eq expect
  end

  it "#adj8_in_range" do
    Point.new.adj8_in_range.should be_a(Iterator(Point))
    a = Point.new(H - 1, W - 1)
    a.adj8_in_range.to_a.should eq [a.up, a.left, a.ul]
    a = Point.new(0, 0)
    a.adj8_in_range.to_a.should eq [a.down, a.right, a.dr]

    res = [] of Point
    a.adj8_in_range { |p| res << p }
    res.should eq [a.down, a.right, a.dr]
  end

  it "#to_s" do
    Point.new(3, 4).to_s.should eq "(3, 4)"
  end

  it "#inspect" do
    Point.new(3, 4).inspect.should eq "(3, 4)"
  end

  it "to_direction_char?" do
    [
      {Point.left * 1, 'L'}, {Point.right * 1, 'R'}, {Point.up * 1, 'U'}, {Point.down * 1, 'D'},
      {Point.left * 9, 'L'}, {Point.right * 9, 'R'}, {Point.up * 9, 'U'}, {Point.down * 9, 'D'},
      {Point.dr, nil},
    ].each do |p, expected|
      p.to_direction_char?.should eq expected
    end

    {"<>^v", "1234"}.each do |str|
      [Point.left, Point.right, Point.up, Point.down].zip(str.chars) do |p, c|
        p.to_direction_char?(str).should eq c
      end
    end
  end

  it ".to_direction?(c : Char)" do
    Point.to_direction?('L').should eq Point.left
    Point.to_direction?('R').should eq Point.right
    Point.to_direction?('U').should eq Point.up
    Point.to_direction?('D').should eq Point.down
    Point.to_direction?('?').should be_nil
  end

  it ".to_direction?(s : String)" do
    [
      {"L", Point.left}, {"R", Point.right}, {"U", Point.up}, {"D", Point.down},
      {"LU", Point.ul}, {"UL", Point.ul}, {"LD", Point.dl}, {"DL", Point.dl},
      {"RU", Point.ur}, {"UR", Point.ur}, {"RD", Point.dr}, {"DR", Point.dr},
      {"?", nil}, {"LL", nil}, {"LR", nil}, {"UD", nil}, {"L?", nil}, {"", nil}, {"LRU", nil},
    ].each do |str, expected|
      Point.to_direction?(str).should eq expected
    end
  end
end

describe Indexable do
  it "#[](point : Point)" do
    a = [[0, 1], [2, 3], [4]]
    [{0, 0, 0}, {0, 1, 1}, {1, 0, 2}, {1, 1, 3}, {2, 0, 4}].each do |y, x, value|
      a[Point.new(y, x)].should eq value
    end
    [{2, 1}, {0, 2}, {0, -1}, {-1, 0}, {3, 0}].each do |y, x|
      expect_raises(IndexError) { a[Point.new(y, x)] }
    end

    b = %w[ab cd e]
    [{0, 0, 'a'}, {0, 1, 'b'}, {1, 0, 'c'}, {1, 1, 'd'}, {2, 0, 'e'}].each do |y, x, char|
      b[Point.new(y, x)].should eq char
    end
    [{2, 1}, {0, 2}, {0, -1}, {-1, 0}, {3, 0}].each do |y, x|
      expect_raises(IndexError) { b[Point.new(y, x)] }
    end
  end

  it "#[]?(point : Point)" do
    a = [[0, 1], [2, 3], [4]]
    [
      {0, 0, 0}, {0, 1, 1}, {1, 0, 2}, {1, 1, 3}, {2, 0, 4},
      {0, 2, nil}, {0, -1, nil}, {-1, 0, nil}, {3, 0, nil},
    ].each do |y, x, value|
      a[Point.new(y, x)]?.should eq value
    end

    b = %w[ab cd e]
    [
      {0, 0, 'a'}, {0, 1, 'b'}, {1, 0, 'c'}, {1, 1, 'd'}, {2, 0, 'e'},
      {2, 1, nil}, {0, 2, nil}, {0, -1, nil}, {-1, 0, nil}, {3, 0, nil},
    ].each do |y, x, value|
      b[Point.new(y, x)]?.should eq value
    end
  end
end

describe Array do
  it "#[]=(point : Point, value)" do
    a = [[0, 1], [2]]
    a[Point.new(0, 0)] = 3
    a[Point.new(0, 1)] = 4
    a[Point.new(1, 0)] = 5
    expect_raises(IndexError) { a[Point.new(2, 1)] = 0 }
    expect_raises(IndexError) { a[Point.new(0, 2)] = 0 }
    expect_raises(IndexError) { a[Point.new(0, -1)] = 0 }
    expect_raises(IndexError) { a[Point.new(-1, 0)] = 0 }
    expect_raises(IndexError) { a[Point.new(3, 0)] = 0 }
    a.should eq [[3, 4], [5]]
  end
end
Back to top page