Hero's formula for the area of a triangle given the length of its three sides    a,   b,   and   c   is given by:

 



A
=


s
(
s
−
a
)
(
s
−
b
)
(
s
−
c
)


,


{\displaystyle A={\sqrt {s(s-a)(s-b)(s-c)}},}


where   s   is half the perimeter of the triangle; that is,

 



s
=



a
+
b
+
c

2


.


{\displaystyle s={\frac {a+b+c}{2}}.}



Heronian triangles
are triangles whose sides and area are all integers.

 An example is the triangle with sides   3, 4, 5   whose area is   6   (and whose perimeter is   12). 


Note that any triangle whose sides are all an integer multiple of   3, 4, 5;   such as   6, 8, 10,   will also be a Heronian triangle.

Define a Primitive Heronian triangle as a Heronian triangle where the greatest common divisor
of all three sides is   1   (unity).

This will exclude, for example, triangle   6, 8, 10.



Task

 Create a named function/method/procedure/... that implements Hero's formula.
 Use the function to generate all the primitive Heronian triangles with sides <= 200.
 Show the count of how many triangles are found.
 Order the triangles by first increasing area, then by increasing perimeter, then by increasing maximum side lengths
 Show the first ten ordered triangles in a table of sides, perimeter, and area.
 Show a similar ordered table for those triangles with area = 210


Show all output here.

Note: when generating triangles it may help to restrict 



a
<=
b
<=
c


{\displaystyle a<=b<=c}



#lang at-exp racket
(require data/order scribble/html)
 
;; Returns the area of a triangle iff the sides have gcd 1, and it is an
;; integer; #f otherwise
(define (heronian?-area a b c)
  (and (= 1 (gcd a b c))
       (let ([s (/ (+ a b c) 2)])  ; ** If s=\frac{a+b+c}{2}
         (and (integer? s)         ; (s must be an integer for the area to b an integer)
              (let-values ([[q r] (integer-sqrt/remainder ; (faster than sqrt)
                                   ; ** Then the area is \sqrt{s(s-a)(s-b)(s-c)}
                                   (* s (- s a) (- s b) (- s c)))])
                (and (zero? r) q)))))) ; (return only integer areas)
 
(define (generate-heronian-triangles max-side)
  (for*/list ([c (in-range 1 (add1 max-side))]
              [b (in-range 1 (add1 c))] ; b<=c
              [a (in-range (add1 (- c b)) (add1 b))] ; ensures a<=b and c<a+b
              [area (in-value (heronian?-area a b c))]
              #:when area)
    ;; datum-order can sort this for the tables (c is the max side length)
    (list area (+ a b c) c (list a b c))))
 
;; Order the triangles by first increasing area, then by increasing perimeter,
;; then by increasing maximum side lengths
(define (tri-sort triangles)
  (sort triangles (λ(t1 t2) (eq? '< (datum-order t1 t2)))))
 
(define (triangles->table triangles)
  (table
   (tr (map th '("#" sides perimeter area))) "\n"
   (for/list ([i (in-naturals 1)] [triangle (in-list triangles)])
     (match-define (list area perimeter max-side sides) triangle)
     (tr (td i) (td (add-between sides ",")) (td perimeter) (td area) "\n"))))
 
(module+ main
  (define ts (generate-heronian-triangles 200))
  (output-xml
   @div{@p{number of primitive triangles found with perimeter @entity{le} 200 = @(length ts)}
        @; Show the first ten ordered triangles in a table of sides, perimeter,
        @; and area.
        @(triangles->table (take (tri-sort ts) 10))
        @; Show a similar ordered table for those triangles with area = 210
        @(triangles->table (tri-sort (filter (λ(t) (eq? 210 (car t))) ts)))
        }))