text: class Triangle(a, b, c) {
  
    has (sides, perimeter, area)
  
    method init {
      sides = [a, b, c].sort
      perimeter = [a, b, c].sum
      var s = (perimeter / 2)
      area = sqrt(s * (s - a) * (s - b) * (s - c))
    }
  
    method is_valid(a,b,c) {
      var (short, middle, long) = [a, b, c].sort...;
      (short + middle) > long
    }
  
    method is_heronian {
      area == area.to_i
    }
  
    method <=>(other) {
      [area, perimeter, sides] <=> [other.area, other.perimeter, other.sides]
    }
  
    method to_s {
      "%-11s%6d%8.1f" % (sides.join('x'), perimeter, area)
    }
  }
  
  var (max, area) = (200, 210)
  var prim_triangles = []
  
  for a in (1..max) {
    for b in (a..max) {
      for c in (b..max) {
        next if (Math.gcd(a, b, c) > 1)
        prim_triangles << Triangle(a, b, c) if Triangle.is_valid(a, b, c)
      }
    }
  }
  
  var sorted = prim_triangles.grep{.is_heronian}.sort
  
  say "Primitive heronian triangles with sides upto #{max}: #{sorted.size}"
  say "\nsides       perim.   area"
  say sorted.first(10).join("\n")
  say "\nTriangles with an area of: #{area}"
  sorted.each{|tr| say tr if (tr.area == area)}
  