Heronian triangles: Difference between revisions

From Rosetta Code
Content added Content deleted
(Added EchoLisp)
(added FreeBasic example)
Line 337: Line 337:
3 148 149 300 210
3 148 149 300 210
</pre>
</pre>
=={{header|FreeBASIC}}==
<lang freebasic>' version 15-09-2015
' compile with: fbc -s console

#Macro header
Print
Print " a b c s area"
Print "-----------------------------------"
#EndMacro

Type triangle
Dim As UInteger a
Dim As UInteger b
Dim As UInteger c
Dim As UInteger s
Dim As UInteger area
End Type

Function gcd(x As UInteger, y As UInteger) As UInteger

Dim As UInteger t

While y
t = y
y = x Mod y
x = t
Wend

Return x

End Function

Function Heronian_triangles(a As UInteger, b As UInteger, c As UInteger, result() As triangle) As UInteger

Dim As UInteger s, sqroot, total, temp

For a = 1 To 200
For b = a To 200
For c = b To 200
' check if a, b and c have a common divisor
If a > 1 And (gcd( a, b) <> 1 And gcd(b, c) <> 1) Then Continue For
s = a + b + c
' temp needs to be even
If (s And 1) = 1 Then Continue For
s \= 2 ' s = s \ 2
If c >= s Then Continue For
temp = s * (s - a) * (s - b) * (s - c)
sqroot = Sqr(temp)
If (sqroot * sqroot) = temp Then
total += 1
'Print a,b,c,s,sqroot
With result(total)
.a = a
.b = b
.c = c
.s = s
.area = sqroot
End With
End If
Next
Next
Next

Return total

End Function

' ------=< MAIN >=------

ReDim result(1 To 10000) As triangle
Dim As UInteger x, y, done, total, inc

total = Heronian_triangles(200, 200, 200, result() )

' trim the array by removing empty entries
ReDim Preserve result(1 To total ) As triangle

' shell sort
' sort order area, s, c
inc = total
Do
inc = IIf(inc > 1, inc \ 2, 1)
Do
done = 0
For x = 1 To total - inc
y = x + inc
If result(x).area > result(y).area Then
Swap result(x), result(y)
done = 1
Else
If result(x).area = result(y).area Then
If result(x).s > result(y).s Then
Swap result(x), result(y)
done = 1
Else
If result(x).s = result(y).s Then
If result(x).c > result(y).c Then
Swap result(x), result(y)
done = 1
End If
End If
End If
End If
End If
Next
Loop Until done = 0
Loop Until inc = 1

Print "There are ";total;" Heronian triangles with sides <= 200"
Print

Print "First ten sorted entries"
header ' print header
For x = 1 To IIf(total > 9, 10, total)
With result(x)
Print Using " ###"; .a; .b; .c; .s; .area
End With
Next
Print
Print

Print "Entries with a area = 210"
header ' print header
For x = 1 To UBound(result)
With result(x)
If .area = 210 Then
Print Using " ###"; .a; .b; .c; .s; .area
End If
End With
Next


' empty keyboard buffer
While Inkey <> "" : Var _key_ = Inkey : Wend
Print : Print "hit any key to end program"
Sleep
End</lang>
{{out}}
<pre>There are 517 Heronian triangles with sides <= 200

First ten sorted entries

a b c s area
-----------------------------------
3 4 5 6 6
5 5 6 8 12
5 5 8 9 12
4 13 15 16 24
5 12 13 15 30
9 10 17 18 36
3 25 26 27 36
7 15 20 21 42
10 13 13 18 60
8 15 17 20 60


Entries with a area = 210

a b c s area
-----------------------------------
17 25 28 35 210
20 21 29 35 210
12 35 37 42 210
17 28 39 42 210
7 65 68 70 210
3 148 149 150 210</pre>


=={{header|Haskell}}==
=={{header|Haskell}}==

Revision as of 19:15, 15 September 2015

Task
Heronian triangles
You are encouraged to solve this task according to the task description, using any language you may know.

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

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

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. This will exclude, for example triangle 6,8,10

The task is to:

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

Show all output here.

Note: when generating triangles it may help to restrict

C#

<lang Csharp> using System; using System.Collections.Generic;

namespace heron {

   class Program{
       static void Main(string[] args){           
           List<int[]> list = new List<int[]>();
           for (int c = 1; c <= 200; c++)
               for (int b = 1; b <= c; b++)
                   for (int a = 1; a <= b; a++)
                       if (gcd(a, gcd(b, c)) == 1 && isHeron(heronArea(a, b, c)))
                           list.Add(new int[] { a, b, c, a + b + c, (int)heronArea(a, b, c)});
           sort(list);
           Console.WriteLine("Number of primitive Heronian triangles with sides up to 200: " + list.Count + "\n\nFirst ten when ordered by increasing area, then perimeter,then maximum sides:\nSides\t\t\tPerimeter\tArea");
           for(int i = 0; i < 10; i++)
               Console.WriteLine(list[i][0] + "\t" + list[i][1] + "\t" + list[i][2] + "\t" + list[i][3] + "\t\t" + list[i][4]);
           Console.WriteLine("\nPerimeter = 210\nSides\t\t\tPerimeter\tArea");
           foreach (int[] i in list)
               if (i[4] == 210)
                   Console.WriteLine(i[0] + "\t" + i[1] + "\t" + i[2] + "\t" + i[3] + "\t\t" + i[4]);     
       }
       static bool isHeron(double heronArea){
           return heronArea % 1 == 0 && heronArea != 0;
       }
       static double heronArea(int a, int b, int c){
           double s = (a + b + c) / 2d;
           return Math.Sqrt(s * (s - a) * (s - b) * (s - c));
       }
       static int gcd(int a, int b){
           int remainder = 1, dividend, divisor;
           dividend = a > b ? a : b;
           divisor = a > b ? b : a;
           while (remainder != 0){
               remainder = dividend % divisor;
               if (remainder != 0){
                   dividend = divisor;
                   divisor = remainder;
               }
           }
           return divisor;
       }
       static void sort(List<int[]> list){
           int[] temp = new int[5];
           bool changed = true;
           while(changed){
               changed = false;
               for (int i = 1; i < list.Count; i++)
                   if (list[i][4] < list[i - 1][4] || list[i][4] == list[i - 1][4] && list[i][3] < list[i - 1][3]){
                       temp = list[i];
                       list[i] = list[i - 1];
                       list[i - 1] = temp;
                       changed = true;
                   }                
           }
       }
   }

} </lang>

Output:

<lang> Number of primitive Heronian triangles with sides up to 200: 517

First ten when ordered by increasing area, then perimeter,then maximum sides: Sides Perimeter Area 3 4 5 12 6 5 5 6 16 12 5 5 8 18 12 4 13 15 32 24 5 12 13 30 30 9 10 17 36 36 3 25 26 54 36 7 15 20 42 42 10 13 13 36 60 8 15 17 40 60

Perimeter = 210 Sides Perimeter Area 17 25 28 70 210 20 21 29 70 210 12 35 37 84 210 17 28 39 84 210 7 65 68 140 210 3 148 149 300 210 </lang>

D

Translation of: Python

<lang d>import std.stdio, std.math, std.range, std.algorithm, std.numeric, std.traits, std.typecons;

double hero(in uint a, in uint b, in uint c) pure nothrow @safe @nogc {

   immutable s = (a + b + c) / 2.0;
   immutable a2 = s * (s - a) * (s - b) * (s - c);
   return (a2 > 0) ? a2.sqrt : 0.0;

}

bool isHeronian(in uint a, in uint b, in uint c) pure nothrow @safe @nogc {

   immutable h = hero(a, b, c);
   return h > 0 && h.floor == h.ceil;

}

T gcd3(T)(in T x, in T y, in T z) pure nothrow @safe @nogc {

   return gcd(gcd(x, y), z);

}

void main() /*@safe*/ {

   enum uint maxSide = 200;
   // Sort by increasing area, perimeter, then sides.
   //auto h = cartesianProduct!3(iota(1, maxSide + 1))
   auto r = iota(1, maxSide + 1);
   const h = cartesianProduct(r, r, r)
             //.filter!({a, b, c} => ...
             .filter!(t => t[0] <= t[1] && t[1] <= t[2] &&
                           t[0] + t[1] > t[2] &&
                           t[].gcd3 == 1 && t[].isHeronian)
             .array
             .schwartzSort!(t => tuple(t[].hero, t[].only.sum, t.reverse))
             .release;
   static void showTriangles(R)(R ts) @safe {
       "Area Perimeter Sides".writeln;
       foreach (immutable t; ts)
           writefln("%3s %8d %3dx%dx%d", t[].hero, t[].only.sum, t[]);
   }
   writefln("Primitive Heronian triangles with sides up to %d: %d", maxSide, h.length);
   "\nFirst ten when ordered by increasing area, then perimeter,then maximum sides:".writeln;
   showTriangles(h.take(10));
   "\nAll with area 210 subject to the previous ordering:".writeln;
   showTriangles(h.filter!(t => t[].hero == 210));

}</lang>

Output:
Primitive Heronian triangles with sides up to 200: 517

First ten when ordered by increasing area, then perimeter,then maximum sides:
Area Perimeter Sides
  6       12   3x4x5
 12       16   5x5x6
 12       18   5x5x8
 24       32   4x13x15
 30       30   5x12x13
 36       36   9x10x17
 36       54   3x25x26
 42       42   7x15x20
 60       36  10x13x13
 60       40   8x15x17

All with area 210 subject to the previous ordering:
Area Perimeter Sides
210       70  17x25x28
210       70  20x21x29
210       84  12x35x37
210       84  17x28x39
210      140   7x65x68
210      300   3x148x149

EchoLisp

<lang scheme>

returns quintuple (A s a b c)
or #f if not hero

(define (hero a b c (s 0) (A 0)) (when (= 1 (gcd a b c)) (set! s (// (+ a b c) 2)) (set! A (* s (- s a)(- s b)(- s c))) (when (square? A) (list (sqrt A) (* s 2) c b a))))

all heroes a,b,c < sidemax
sorted by A|s|c & a <=b <= c

(define (heroes (sidemax 201)) (list-sort/fields 3 (for*/list ((a (in-range 1 sidemax)) (b (in-range a sidemax)) (c (in-range b sidemax))) #:continue (<= (+ a b) c) ;; triangle inequality must hold !! cut search #:continue (not (hero a b c)) (hero a b c))))

(define (print-hero h) (printf "A: %6d s: %6d sides: %dx%dx%d" (list-ref h 0) (list-ref h 1) (list-ref h 2)(list-ref h 3) (list-ref h 4))) (define (print-laurels H) (writeln '🌿🌿 (length H) 'heroes '🌿🌿)) </lang>

Output:
(define H (heroes))

(print-laurels H)
🌿🌿     517     heroes     🌿🌿    

(for-each print-hero (take H 10))

A:      6 s:     12 sides: 5x4x3
A:     12 s:     16 sides: 6x5x5
A:     12 s:     18 sides: 8x5x5
A:     24 s:     32 sides: 15x13x4
A:     30 s:     30 sides: 13x12x5
A:     36 s:     36 sides: 17x10x9
A:     36 s:     54 sides: 26x25x3
A:     42 s:     42 sides: 20x15x7
A:     60 s:     36 sides: 13x13x10
A:     60 s:     40 sides: 17x15x8

(for-each print-hero (filter (lambda(h) (= 210 (first h))) H))

A:    210 s:     70 sides: 28x25x17
A:    210 s:     70 sides: 29x21x20
A:    210 s:     84 sides: 37x35x12
A:    210 s:     84 sides: 39x28x17
A:    210 s:    140 sides: 68x65x7
A:    210 s:    300 sides: 149x148x3

ERRE

<lang ERRE> PROGRAM HERON

DIM LISTA%[600,4]

PROCEDURE GCD(J%,K%->MCD%)

 WHILE J%<>K% DO
    IF J%>K% THEN
        J%=J%-K%
      ELSE
        K%=K%-J%
    END IF
 END WHILE
 MCD%=J%

END PROCEDURE

BEGIN

   PRINT(CHR$(12);) !CLS
   FOR C%=1 TO 200 DO
      FOR B%=1 TO C% DO
         FOR A%=1 TO B% DO
            S#=(A%+B%+C%)/2#
            AREA#=S#*(S#-A%)*(S#-B%)*(S#-C%)
            IF AREA#>0 THEN
            AREA#=SQR(AREA#)
            IF AREA#=INT(AREA#) THEN
                GCD(B%,C%->RES%)
                GCD(A%,RES%->RES%)
                   IF RES%=1 THEN
                      COUNT%=COUNT%+1
                      LISTA%[COUNT%,0]=A%    LISTA%[COUNT%,1]=B%   LISTA%[COUNT%,2]=C%
                      LISTA%[COUNT%,3]=2*S#  LISTA%[COUNT%,4]=AREA#
                   END IF
            END IF
        END IF
    END FOR
 END FOR

END FOR

PRINT("Number of triangles:";COUNT%)

! sorting array FLIPS%=TRUE WHILE FLIPS% DO

  FLIPS%=FALSE
  FOR I%=1 TO COUNT%-1 DO
     IF LISTA%[I%,4]>LISTA%[I%+1,4] THEN
       FOR K%=0 TO 4 DO
          SWAP(LISTA%[I%,K%],LISTA%[I%+1,K%])
       END FOR
       FLIPS%=TRUE
     END IF
  END FOR

END WHILE

! first ten FOR I%=1 TO 10 DO

   PRINT(#1,LISTA%[I%,0],LISTA%[I%,1],LISTA%[I%,2],LISTA%[I%,3],LISTA%[I%,4])

END FOR PRINT

! triangle with area=210 FOR I%=1 TO COUNT% DO

   IF LISTA%[I%,4]=210 THEN
      PRINT(LISTA%[I%,0],LISTA%[I%,1],LISTA%[I%,2],LISTA%[I%,3],LISTA%[I%,4])
   END IF

END FOR END PROGRAM </lang>

Number of triangles: 517
 3             4             5             12            6
 5             5             6             16            12
 5             5             8             18            12
 4             13            15            32            24
 5             12            13            30            30
 9             10            17            36            36
 3             25            26            54            36
 7             15            20            42            42
 10            13            13            36            60
 8             15            17            40            60

 17            25            28            70            210
 20            21            29            70            210
 12            35            37            84            210
 17            28            39            84            210
 7             65            68            140           210
 3             148           149           300           210

FreeBASIC

<lang freebasic>' version 15-09-2015 ' compile with: fbc -s console

  1. Macro header
   Print
   Print "      a      b      c      s   area"
   Print "-----------------------------------"
  1. EndMacro

Type triangle

   Dim As UInteger a
   Dim As UInteger b
   Dim As UInteger c
   Dim As UInteger s
   Dim As UInteger area

End Type

Function gcd(x As UInteger, y As UInteger) As UInteger

   Dim As UInteger t
   While y
       t = y
       y = x Mod y
       x = t
   Wend
   Return x

End Function

Function Heronian_triangles(a As UInteger, b As UInteger, c As UInteger, result() As triangle) As UInteger

   Dim As UInteger s, sqroot, total, temp
   For a = 1 To 200
       For b = a To 200
           For c = b To 200
               ' check if a, b and c have a common divisor
               If a > 1 And (gcd( a, b) <> 1 And gcd(b, c) <> 1) Then Continue For
               s = a + b + c
               ' temp needs to be even
               If (s And 1) = 1 Then Continue For
               s \= 2 ' s = s \ 2
               If c >= s Then Continue For
               temp = s * (s - a) * (s - b) * (s - c)
               sqroot = Sqr(temp)
               If (sqroot * sqroot) = temp Then
                   total += 1
                   'Print a,b,c,s,sqroot
                   With result(total)
                       .a = a
                       .b = b
                       .c = c
                       .s = s
                       .area = sqroot
                   End With
               End If
           Next
       Next
   Next
   Return total

End Function

' ------=< MAIN >=------

ReDim result(1 To 10000) As triangle Dim As UInteger x, y, done, total, inc

total = Heronian_triangles(200, 200, 200, result() )

' trim the array by removing empty entries ReDim Preserve result(1 To total ) As triangle

' shell sort ' sort order area, s, c inc = total Do

   inc = IIf(inc > 1, inc \ 2, 1)
   Do
       done = 0
       For x = 1 To total - inc
           y = x + inc
           If result(x).area > result(y).area Then
               Swap result(x), result(y)
               done = 1
           Else
               If result(x).area = result(y).area Then
                   If result(x).s > result(y).s Then
                       Swap result(x), result(y)
                       done = 1
                   Else
                       If result(x).s = result(y).s Then
                           If result(x).c > result(y).c Then
                               Swap result(x), result(y)
                               done = 1
                           End If
                       End If
                   End If
               End If
           End If
       Next
   Loop Until done = 0

Loop Until inc = 1

Print "There are ";total;" Heronian triangles with sides <= 200" Print

Print "First ten sorted entries" header ' print header For x = 1 To IIf(total > 9, 10, total)

   With result(x)
       Print Using "    ###"; .a; .b; .c; .s; .area
   End With

Next Print Print

Print "Entries with a area = 210" header ' print header For x = 1 To UBound(result)

   With result(x)
       If .area = 210 Then
           Print Using "    ###"; .a; .b; .c; .s; .area
       End If
   End With

Next


' empty keyboard buffer While Inkey <> "" : Var _key_ = Inkey : Wend Print : Print "hit any key to end program" Sleep End</lang>

Output:
There are 517 Heronian triangles with sides <= 200

First ten sorted entries

      a      b      c      s   area
-----------------------------------
      3      4      5      6      6
      5      5      6      8     12
      5      5      8      9     12
      4     13     15     16     24
      5     12     13     15     30
      9     10     17     18     36
      3     25     26     27     36
      7     15     20     21     42
     10     13     13     18     60
      8     15     17     20     60


Entries with a area = 210

      a      b      c      s   area
-----------------------------------
     17     25     28     35    210
     20     21     29     35    210
     12     35     37     42    210
     17     28     39     42    210
      7     65     68     70    210
      3    148    149    150    210

Haskell

<lang Haskell>import qualified Data.List as L import Data.Maybe import Data.Ord import Text.Printf

-- Determine if a number n is a perfect square and return its square root if so. -- This is used instead of sqrt to avoid fixed sized floating point numbers. perfectSqrt :: Integral a => a -> Maybe a perfectSqrt n

 | n == 1    = Just 1
 | n < 4     = Nothing
 | otherwise =
 let search low high =
       let guess = (low + high) `div` 2
           square = guess ^ 2
           next
             | square == n  = Just guess
             | low == guess = Nothing
             | square < n   = search guess high
             | otherwise    = search low guess
       in next
 in search 0 n

-- Determine the area of a Heronian triangle if it is one. heronTri :: Integral a => a -> a -> a -> Maybe a heronTri a b c =

 let -- Rewrite Heron's formula to factor out the term 16 under the root.
   areaSq16    = (a + b + c) * (b + c - a) * (a + c - b) * (a + b - c)
   (areaSq, r) = areaSq16 `divMod` 16
 in if r == 0
    then perfectSqrt areaSq
    else Nothing

isPrimitive :: Integral a => a -> a -> a -> a isPrimitive a b c = gcd a (gcd b c)

third (_, _, x, _, _) = x fourth (_, _, _, x, _) = x fifth (_, _, _, _, x) = x

orders :: Ord b => [(a -> b)] -> a -> a -> Ordering orders [f] a b = comparing f a b orders (f:fx) a b =

 case comparing f a b of
   EQ -> orders fx a b
   n  -> n

main :: IO () main = do

 let range = [1 .. 200]
     tris :: [(Integer, Integer, Integer, Integer, Integer)]
     tris = L.sortBy (orders [fifth, fourth, third])
            $ map (\(a, b, c, d, e) -> (a, b, c, d, fromJust e))
            $ filter (isJust . fifth)
            [(a, b, c, a + b + c, heronTri a b c)
            | a <- range, b <- range, c <- range
            , a <= b, b <= c, isPrimitive a b c == 1]
     printTri (a, b, c, d, e) = printf "%3d %3d %3d %9d %4d\n" a b c d e
 printf "Heronian triangles found: %d\n\n" $ length tris
 putStrLn "   Sides    Perimeter Area"
 mapM_ printTri $ take 10 tris
 putStrLn ""
 mapM_ printTri $ filter ((== 210) . fifth) tris</lang>
Output:
Heronian triangles found: 517

   Sides    Perimeter Area
  3   4   5        12    6
  5   5   6        16   12
  5   5   8        18   12
  4  13  15        32   24
  5  12  13        30   30
  9  10  17        36   36
  3  25  26        54   36
  7  15  20        42   42
 10  13  13        36   60
  8  15  17        40   60

 17  25  28        70  210
 20  21  29        70  210
 12  35  37        84  210
 17  28  39        84  210
  7  65  68       140  210
  3 148 149       300  210

J

Hero's formula Implementation

<lang J>a=: 0&{"1 b=: 1&{"1 c=: 2&{"1 s=: (a+b+c) % 2: area=: 2 %: s*(s-a)*(s-b)*(s-c) NB. Hero's formula perim=: +/"1 isPrimHero=: (0&~: * (= <.@:+))@area * 1 = a +. b +. c</lang>

Alternative Implementation

The implementation above uses the symbols as given in the formulae at the top of the page, making it easier to follow along as well as spot any errors. The formulae distinguish between the individual sides of the triangles whereas J would normally treat these as a single entity or array. The implementation below uses this "normal J" approach:

<lang J>perim=: +/"1 s=: -:@:perim area=: [: %: s * [: */"1 s - ] NB. Hero's formula isNonZeroInt=: 0&~: *. (= <.@:+) isPrimHero=: isNonZeroInt@area *. 1 = +./&.|:</lang>

Required tasks

<lang J> Tri=: ~. /:"1~ 1 + 200 200 200 #: i. 200^3 NB. distinct triangles with sides <= 200

  HeroTri=: (#~ isPrimHero) Tri                  NB. all primitive Heronian triangles with sides <= 200
  # HeroTri                                      NB. count triangles found

517

  HeroTri=: (/: area ,. perim ,. ]) HeroTri      NB. sort by area, perimeter & sides
  (,. _ ,. perim ,. area) 10 {. HeroTri          NB. tabulate sides, perimeter & area for top 10 triangles
3  4  5 _ 12  6
5  5  6 _ 16 12
5  5  8 _ 18 12
4 13 15 _ 32 24
5 12 13 _ 30 30
9 10 17 _ 36 36
3 25 26 _ 54 36
7 15 20 _ 42 42

10 13 13 _ 36 60

8 15 17 _ 40 60
  (,. _ ,. perim ,. area) (#~ 210 = area) HeroTri NB. tablulate sides, perimeter & area for triangles with area = 210

17 25 28 _ 70 210 20 21 29 _ 70 210 12 35 37 _ 84 210 17 28 39 _ 84 210

7  65  68 _ 140 210
3 148 149 _ 300 210</lang>

Java

<lang Java> import java.util.ArrayList; public class Heron { public static void main(String[] args) { ArrayList<int[]> list = new ArrayList<int[]>(); for(int c = 1; c <= 200; c++){ for(int b = 1; b <= c; b++){ for(int a = 1; a <= b; a++){ if(gcd(gcd(a, b), c) == 1 && isHeron(heronArea(a, b, c) list.add(new int[]{a, b, c, a + b + c, (int)heronArea(a, b, c)}); } } } sort(list); System.out.printf("Number of primitive Heronian triangles with sides up to 200: %d\n\nFirst ten when ordered by increasing area, then perimeter:\nSides Perimeter Area", list.size()); for(int i = 0; i < 10; i++){ System.out.printf("\n%d x %d x %d %d %d",list.get(i)[0], list.get(i)[1], list.get(i)[2], list.get(i)[3], list.get(i)[4]); } System.out.printf("\n\nArea = 210\nSides Perimeter Area"); for(int i = 0; i < list.size(); i++){ if(list.get(i)[4] == 210) System.out.printf("\n%d x %d x %d %d %d",list.get(i)[0], list.get(i)[1], list.get(i)[2], list.get(i)[3], list.get(i)[4]); } } public static double heronArea(int a, int b, int c){ double s = (a + b + c)/ 2f; return Math.sqrt(s *(s -a)*(s - b)*(s - c)); } public static boolean isHeron(double h){ return h % 1 == 0 && h > 0; } public static int gcd(int a, int b){ int leftover = 1, dividend = a > b ? a : b, divisor = a > b ? b : a; while(leftover != 0){ leftover = dividend % divisor; if(leftover > 0){ dividend = divisor; divisor = leftover; } } return divisor; } public static void sort(ArrayList<int[]> list){ boolean swapped = true; int[] temp; while(swapped){ swapped = false; for(int i = 1; i < list.size(); i++){ if(list.get(i)[4] < list.get(i - 1)[4] || list.get(i)[4] == list.get(i - 1)[4] && list.get(i)[3] < list.get(i - 1)[3]){ temp = list.get(i); list.set(i, list.get(i - 1)); list.set(i - 1, temp); swapped = true; } } } } } </lang>

Output:

<lang> Number of primitive Heronian triangles with sides up to 200: 517

First ten when ordered by increasing area, then perimeter: Sides Perimeter Area 3 x 4 x 5 12 6 5 x 5 x 6 16 12 5 x 5 x 8 18 12 4 x 13 x 15 32 24 5 x 12 x 13 30 30 9 x 10 x 17 36 36 3 x 25 x 26 54 36 7 x 15 x 20 42 42 10 x 13 x 13 36 60 8 x 15 x 17 40 60

Area = 210 Sides Perimeter Area 17 x 25 x 28 70 210 20 x 21 x 29 70 210 12 x 35 x 37 84 210 17 x 28 x 39 84 210 7 x 65 x 68 140 210 3 x 148 x 149 300 210 </lang>

JavaScript

<lang JavaScript> window.onload = function(){

   var list = [];
   var j = 0;	
   for(var c = 1; c <= 200; c++)
       for(var b = 1; b <= c; b++)
           for(var a = 1; a <= b; a++)

if(gcd(gcd(a, b), c) == 1 && isHeron(heronArea(a, b, c))) list[j++] = new Array(a, b, c, a + b + c, heronArea(a, b, c));

   sort(list);	

document.write("

Primitive Heronian triangles with sides up to 200: " + list.length + "

First ten when ordered by increasing area, then perimeter:

");
   for(var i = 0; i < 10; i++)
document.write(""); document.write("
SidesPerimeterArea
" + list[i][0] + " x " + list[i][1] + " x " + list[i][2] + "" + list[i][3] + "" + list[i][4] + "

Area = 210

");
   for(var i = 0; i < list.length; i++)

if(list[i][4] == 210)

document.write("");
   function heronArea(a, b, c){

var s = (a + b + c)/ 2; return Math.sqrt(s *(s -a)*(s - b)*(s - c));

   }	
   function isHeron(h){
       return h % 1 == 0 && h > 0;
   }	
   function gcd(a, b){

var leftover = 1, dividend = a > b ? a : b, divisor = a > b ? b : a; while(leftover != 0){ leftover = dividend % divisor; if(leftover > 0){ dividend = divisor; divisor = leftover; } } return divisor;

   }	
   function sort(list){

var swapped = true; var temp = []; while(swapped){ swapped = false; for(var i = 1; i < list.length; i++){ if(list[i][4] < list[i - 1][4] || list[i][4] == list[i - 1][4] && list[i][3] < list[i - 1][3]){ temp = list[i]; list[i] = list[i - 1]; list[i - 1] = temp; swapped = true; } } }

   }

} </lang>

Output:

<lang> Primitive Heronian triangles with sides up to 200: 517

First ten when ordered by increasing area, then perimeter: Sides Perimeter Area 3 x 4 x 5 12 6 5 x 5 x 6 16 12 5 x 5 x 8 18 12 4 x 13 x 15 32 24 5 x 12 x 13 30 30 9 x 10 x 17 36 36 3 x 25 x 26 54 36 7 x 15 x 20 42 42 10 x 13 x 13 36 60 8 x 15 x 17 40 60

Area = 210 Sides Perimeter Area 17 x 25 x 28 70 210 20 x 21 x 29 70 210 12 x 35 x 37 84 210 17 x 28 x 39 84 210 7 x 65 x 68 140 210 3 x 148 x 149 300 210 </lang>

jq

Works with: jq version 1.4

<lang jq># input should be an array of the lengths of the sides def hero:

 (add/2) as $s
 | ($s*($s - .[0])*($s - .[1])*($s - .[2])) as $a2
 | if $a2 > 0 then ($a2 | sqrt) else 0 end;

def is_heronian:

 hero as $h
 | $h > 0 and ($h|floor) == $h;

def gcd3(x; y; z):

 # subfunction expects [a,b] as input
 def rgcd:
   if .[1] == 0 then .[0]
   else [.[1], .[0] % .[1]] | rgcd
   end;
 [ ([x,y] | rgcd), z ] | rgcd;

def task(maxside):

 def rjust(width): tostring |  " " * (width - length) + .;
 
 [ range(1; maxside+1) as $c
   | range(1; $c+1) as $b
   | range(1; $b+1) as $a
   | if ($a + $b) > $c and gcd3($a; $b; $c) == 1
     then [$a,$b,$c] | if is_heronian then . else empty end
     else empty
     end ]
 # sort by increasing area, perimeter, then sides
 | sort_by( [ hero, add, .[2] ] )  
 | "The number of primitive Heronian triangles with sides up to \(maxside): \(length)",
   "The first ten when ordered by increasing area, then perimeter, then maximum sides:",
   "      perimeter area",
   (.[0:10][] | "\(rjust(11)) \(add | rjust(3)) \(hero | rjust(4))" ),
   "All those with area 210, ordered as previously:",
   "      perimeter area",
   ( .[] | select( hero == 210 ) | "\(rjust(11)) \(add|rjust(3)) \(hero|rjust(4))" ) ;

task(200)</lang>

Output:

<lang sh>$ time jq -n -r -f heronian.jq The number of primitive Heronian triangles with sides up to 200: 517 The first ten when ordered by increasing area, then perimeter, then maximum sides:

     perimeter area
   [3,4,5]  12    6
   [5,5,6]  16   12
   [5,5,8]  18   12
 [4,13,15]  32   24
 [5,12,13]  30   30
 [9,10,17]  36   36
 [3,25,26]  54   36
 [7,15,20]  42   42
[10,13,13]  36   60
 [8,15,17]  40   60

All those with area 210, ordered as previously:

     perimeter area
[17,25,28]  70  210
[20,21,29]  70  210
[12,35,37]  84  210
[17,28,39]  84  210
 [7,65,68] 140  210

[3,148,149] 300 210</lang>

Julia

The type IntegerTriangle stores a triangle's sides (a, b, c), perimeter (p) and area (σ) as integers. The function isprimheronian checks whether the a triangle of integer sides is a primitive Heronian triangle and is called prior to construction of an IntegerTriangle.

Types and Functions <lang Julia> type IntegerTriangle{T<:Integer}

   a::T
   b::T
   c::T
   p::T
   σ::T

end

function IntegerTriangle{T<:Integer}(a::T, b::T, c::T)

   p = a + b + c
   s = div(p, 2)
   σ = isqrt(s*(s-a)*(s-b)*(s-c))
   (x, y, z) = sort([a, b, c])
   IntegerTriangle(x, y, z, p, σ)

end

function isprimheronian{T<:Integer}(a::T, b::T, c::T)

   p = a + b + c
   iseven(p) || return false
   gcd(a, b, c) == 1 || return false
   s = div(p, 2)
   t = s*(s-a)*(s-b)*(s-c)
   0 < t || return false
   σ = isqrt(t)
   σ^2 == t

end </lang>

Main <lang Julia> slim = 200

ht = IntegerTriangle[]

for a in 1:slim, b in a:slim, c in b:slim

   isprimheronian(a, b, c) || continue
   push!(ht, IntegerTriangle(a, b, c))

end

sort!(ht, by=x->(x.σ, x.p, x.c))

print("The number of primitive Hernonian triangles having sides ≤ ") println(slim, " is ", length(ht))

tlim = 10 tlim = min(tlim, length(ht))

println() println("Tabulating the first (by σ, p, c) ", tlim, " of these:") println(" a b c σ p") for t in ht[1:tlim]

   println(@sprintf "%6d %3d %3d %4d %4d" t.a t.b t.c t.σ t.p)

end

tlim = 210 println() println("Tabulating those having σ = ", tlim, ":") println(" a b c σ p") for t in ht

   t.σ == tlim || continue
   t.σ == tlim || break
   println(@sprintf "%6d %3d %3d %4d %4d" t.a t.b t.c t.σ t.p)

end </lang>

Output:
The number of primitive Hernonian triangles having sides ≤ 200 is 517

Tabulating the first (by σ, p, c) 10 of these:
    a   b   c    σ    p
     3   4   5    6   12
     5   5   6   12   16
     5   5   8   12   18
     4  13  15   24   32
     5  12  13   30   30
     9  10  17   36   36
     3  25  26   36   54
     7  15  20   42   42
    10  13  13   60   36
     8  15  17   60   40

Tabulating those having σ = 210:
    a   b   c    σ    p
    17  25  28  210   70
    20  21  29  210   70
    12  35  37  210   84
    17  28  39  210   84
     7  65  68  210  140
     3 148 149  210  300

Nim

<lang nim>import math, algorithm, strfmt, sequtils

type

 HeronianTriangle = tuple  
   a: int  
   b: int  
   c: int
   s: float
   A: int

proc `$` (t: HeronianTriangle): string =

 fmt("{:3d}, {:3d}, {:3d}\t{:3.3f}\t{:3d}", t.a, t.b, t.c, t.s, t.A)
 

proc hero(a:int, b:int, c:int): tuple[s, A: float] =

 let s: float = (a + b + c) / 2
 result = (s, sqrt( s * (s - float(a)) * (s - float(b)) * (s - float(c)) ))

proc isHeronianTriangle(x: float): bool = ceil(x) == x and x.toInt > 0

proc gcd(x: int, y: int): int =

 var
   (dividend, divisor) = if x > y: (x, y) else: (y, x)
   remainder = dividend mod divisor
   
 while remainder != 0:
   dividend = divisor
   divisor = remainder
   remainder = dividend mod divisor
 result = divisor
           
           

var list = newSeq[HeronianTriangle]() const max = 200

for c in 1..max:

 for b in 1..c:
   for a in 1..b:
     let (s, A) = hero(a, b, c)
     if isHeronianTriangle(A) and gcd(a, gcd(b, c)) == 1:
       let t:HeronianTriangle = (a, b, c, s, A.toInt)
       list.add(t)

echo "Numbers of Heronian Triangle : ", list.len

list.sort do (x, y: HeronianTriangle) -> int:

 result = cmp(x.A, y.A)
 if result == 0:
   result = cmp(x.s, y.s)
   if result == 0:
     result = cmp(max(x.a, x.b, x.c), max(y.a, y.b, y.c))

echo "Ten first Heronian triangle ordered : " echo "Sides Perimeter Area" for t in list[0 .. <10]:

 echo t

echo "Heronian triangle ordered with Area 210 : " echo "Sides Perimeter Area" for t in list.filter(proc (x: HeronianTriangle): bool = x.A == 210):

 echo t</lang>
Output:
Numbers of Heronian Triangle : 517
Ten first Heronian triangle ordered :
Sides          Perimeter Area
  3,   4,   5	6.000	  6
  5,   5,   6	8.000	 12
  5,   5,   8	9.000	 12
  4,  13,  15	16.000	 24
  5,  12,  13	15.000	 30
  9,  10,  17	18.000	 36
  3,  25,  26	27.000	 36
  7,  15,  20	21.000	 42
 10,  13,  13	18.000	 60
  8,  15,  17	20.000	 60
Heronian triangle ordered with Area 210 :
Sides          Perimeter Area
 17,  25,  28	35.000	210
 20,  21,  29	35.000	210
 12,  35,  37	42.000	210
 17,  28,  39	42.000	210
  7,  65,  68	70.000	210
  3, 148, 149	150.000	210

ooRexx

Derived from REXX with some changes <lang rexx>/*REXX pgm generates primitive Heronian triangles by side length & area.*/

 Call time 'R'
 Numeric Digits 12
 Parse Arg mxs area list
 If mxs = Then mxs =200
 If area= Then area=210
 If list= Then list=10
 tx='primitive Heronian triangles'
 Call heronian mxs            /* invoke sub with max SIDES.     */
 Say nt tx 'found with side length up to' mxs "(inclusive)."
 Call show '2'
 Call show '3'
 Say time('E') 'seconds elapsed'
 Exit

heronian:

 abc.=0  /* abc.ar.p.* contains 'a b c' for area ar and perimeter p */
 nt=0                              /* number of triangles found     */
 min.=
 max.=
 mem.=0
 ln=length(mxs)
 Do a=3 To mxs
   Do b=a To mxs
     ab=a+b
     Do c=b To mxs
       If hgcd(a,b,c)=1 Then Do    /* GCD=1                         */
         ar=heron_area()
         If pos('.',ar)=0 Then Do  /* is an integer                 */
           nt=nt+1                 /* a primitive Heronian triangle.*/
           Call minmax '0P',p
           Call minmax '0A',a
           per=ab+c
           abc_ar=right(per,4) right(a,4) right(b,4) right(c,4),
                                                           right(ar,5)
           Call mem abc_ar
           End
         End
       End
     End
   End
 /*
 say 'min.p='min.0p
 say 'max.p='max.0p
 say 'min.a='min.0a
 say 'max.a='max.0a
 */
 Return nt

hgcd: Procedure

 Parse Arg x
 Do j=2 For 2
   y=arg(j)
   Do Until _==0
     _=x//y
     x=y
     y=_
     End
   End
 Return x

minmax:

 Parse Arg which,x
 If min.which= Then Do
   min.which=x
   max.which=x
   End
 Else Do
   min.which=min(min.which,x)
   max.which=max(max.which,x)
   End
 --Say which min.which '-' max.which
 Return

heron_area:

 p=ab+c                           /* perimeter                      */
 s=p/2
 ar2=s*(s-a)*(s-b)*(s-c)          /* area**2                        */
 If pos(right(ar2,1),'014569')=0 Then /* ar2 cannot be              */
   Return '.'                         /* square of an integer*/
 If ar2>0 Then
   ar=sqrt(ar2)                   /* area                           */
 Else
   ar='.'
 Return ar

show: Parse Arg which

 Say 
 Select
   When which='2' Then Do
     Say 'Listing of the first' list tx":"
     Do i=1 To list
       Call ot i,mem.i
       End
     End
   When which='3' Then Do
     Say 'Listing of the' tx "with area=210"
     j=0
     Do i=1 To mem.0
       Parse Var mem.i per a b c area
       If area=210 Then Do
         j=j+1
         Call ot j,mem.i
         End
       End
     End
   End
 Return

ot: Parse Arg k,mem

   Parse Var mem per a b c area
   Say right(k,9)'     area:'right(area,6)||,
               '      perimeter:'right(per,4)'     sides:',
                      right(a,3) right(b,3) right(c,3)
   Return

mem:

 Parse Arg e
 Do i=1 To mem.0
   If mem.i>>e Then Leave
   End
 Do j=mem.0 to i By -1
   j1=j+1
   mem.j1=mem.j
   End
 mem.i=e
 mem.0=mem.0+1
 Return

/* for "Classic" REXX sqrt: procedure; parse arg x;if x=0 then return 0;d=digits();numeric digits 11 numeric form; parse value format(x,2,1,,0) 'E0' with g 'E' _ .; g=g*.5'E'_%2 p=d+d%4+2; m.=11; do j=0 while p>9; m.j=p; p=p%2+1; end; do k=j+5 to 0 by -1 if m.k>11 then numeric digits m.k;g=.5*(g+x/g);end;numeric digits d;return g/1

  • /

/* for ooRexx */

requires rxmath library
routine sqrt
 Return rxCalcSqrt(arg(1),14)</lang>
Output:
517 primitive Heronian triangles found with side length up to 200 (inclusive).

Listing of the first 10 primitive Heronian triangles:
        1     area:     6      perimeter:  12     sides:   3   4   5
        2     area:    12      perimeter:  16     sides:   5   5   6
        3     area:    12      perimeter:  18     sides:   5   5   8
        4     area:    30      perimeter:  30     sides:   5  12  13
        5     area:    24      perimeter:  32     sides:   4  13  15
        6     area:    36      perimeter:  36     sides:   9  10  17
        7     area:    60      perimeter:  36     sides:  10  13  13
        8     area:    60      perimeter:  40     sides:   8  15  17
        9     area:    42      perimeter:  42     sides:   7  15  20
       10     area:    84      perimeter:  42     sides:  13  14  15

Listing of the primitive Heronian triangles with area=210
        1     area:   210      perimeter:  70     sides:  17  25  28
        2     area:   210      perimeter:  70     sides:  20  21  29
        3     area:   210      perimeter:  84     sides:  12  35  37
        4     area:   210      perimeter:  84     sides:  17  28  39
        5     area:   210      perimeter: 140     sides:   7  65  68
        6     area:   210      perimeter: 300     sides:   3 148 149
26.054000 seconds elapsed 

PARI/GP

<lang parigp>Heron(v)=my([a,b,c]=v); (a+b+c)*(-a+b+c)*(a-b+c)*(a+b-c) \\ returns 16 times the squared area is(a,b,c)=(a+b+c)%2==0 && gcd(a,gcd(b,c))==1 && issquare(Heron([a,b,c])) v=List(); for(a=1,200,for(b=a+1,200,for(c=b+1,200, if(is(a,b,c),listput(v, [a,b,c]))))); v=Vec(v); #v vecsort(v, (a,b)->Heron(a)-Heron(b))[1..10] vecsort(v, (a,b)->vecsum(a)-vecsum(b))[1..10] vecsort(v, 3)[1..10] \\ shortcut: order by third component u=select(v->Heron(v)==705600, v); vecsort(u, (a,b)->Heron(a)-Heron(b)) vecsort(u, (a,b)->vecsum(a)-vecsum(b)) vecsort(u, 3) \\ shortcut: order by third component</lang>

Output:
%1 = [[1, 2, 3], [1, 3, 4], [1, 4, 5], [1, 5, 6], [1, 6, 7], [1, 7, 8], [1, 8, 9], [1, 9, 10], [1, 10, 11], [1, 11, 12]]
%2 = [[1, 2, 3], [1, 3, 4], [1, 4, 5], [2, 3, 5], [1, 5, 6], [3, 4, 5], [1, 6, 7], [2, 5, 7], [3, 4, 7], [1, 7, 8]]
%3 = [[1, 2, 3], [1, 3, 4], [1, 4, 5], [2, 3, 5], [3, 4, 5], [1, 5, 6], [1, 6, 7], [2, 5, 7], [3, 4, 7], [1, 7, 8]]
%4 = [[3, 148, 149], [7, 65, 68], [12, 35, 37], [17, 25, 28], [17, 28, 39], [20, 21, 29]]
%5 = [[17, 25, 28], [20, 21, 29], [12, 35, 37], [17, 28, 39], [7, 65, 68], [3, 148, 149]]
%6 = [[17, 25, 28], [20, 21, 29], [12, 35, 37], [17, 28, 39], [7, 65, 68], [3, 148, 149]]

Perl

Translation of: Perl 6

<lang perl>use strict; use warnings; use List::Util qw(max);

sub gcd { $_[1] == 0 ? $_[0] : gcd($_[1], $_[0] % $_[1]) }

sub hero {

   my ($a, $b, $c) = @_[0,1,2];
   my $s = ($a + $b + $c) / 2;
   sqrt $s*($s - $a)*($s - $b)*($s - $c);

}

sub heronian_area {

   my $hero = hero my ($a, $b, $c) = @_[0,1,2];
   sprintf("%.0f", $hero) eq $hero ? $hero : 0

}

sub primitive_heronian_area {

   my ($a, $b, $c) = @_[0,1,2];
   heronian_area($a, $b, $c) if 1 == gcd $a, gcd $b, $c;

}

sub show {

   print "   Area Perimeter   Sides\n";
   for (@_) {
       my ($area, $perim, $c, $b, $a) = @$_;

printf "%7d %9d %d×%d×%d\n", $area, $perim, $a, $b, $c;

   }

}

sub main {

   my $maxside = shift // 200;
   my $first = shift // 10;
   my $witharea = shift // 210;
   my @h;
   for my $c (1 .. $maxside) {

for my $b (1 .. $c) { for my $a ($c - $b + 1 .. $b) { if (my $area = primitive_heronian_area $a, $b, $c) { push @h, [$area, $a+$b+$c, $c, $b, $a]; } } }

   }
   @h = sort {

$a->[0] <=> $b->[0] or $a->[1] <=> $b->[1] or max(@$a[2,3,4]) <=> max(@$b[2,3,4])

   } @h;
   printf "Primitive Heronian triangles with sides up to %d: %d\n",
   $maxside,
   scalar @h;
   print "First:\n";
   show @h[0 .. $first - 1];
   print "Area $witharea:\n";
   show grep { $_->[0] == $witharea } @h;


}

&main();</lang>

Output:
Primitive Heronian triangles with sides up to 200: 517
First:
   Area Perimeter   Sides
      6        12    3×4×5
     12        16    5×5×6
     12        18    5×5×8
     24        32    4×13×15
     30        30    5×12×13
     36        36    9×10×17
     36        54    3×25×26
     42        42    7×15×20
     60        36    10×13×13
     60        40    8×15×17
Area 210:
   Area Perimeter   Sides
    210        70    17×25×28
    210        70    20×21×29
    210        84    12×35×37
    210        84    17×28×39
    210       140    7×65×68
    210       300    3×148×149

Perl 6

Works with: rakudo version 2015-01-03

<lang perl6>sub hero($a, $b, $c) {

   my $s = ($a + $b + $c) / 2;
   my $a2 = $s * ($s - $a) * ($s - $b) * ($s - $c);
   $a2.sqrt;

}

sub heronian-area($a, $b, $c) {

   $_ when Int given hero($a, $b, $c).narrow;

}

sub primitive-heronian-area($a, $b, $c) {

   heronian-area $a, $b, $c
       if 1 == [gcd] $a, $b, $c;

}

sub show {

   say "   Area Perimeter   Sides";
   for @_ -> [$area, $perim, $c, $b, $a] {

printf "%6d %6d %12s\n", $area, $perim, "$a×$b×$c";

   }

}

sub MAIN ($maxside = 200, $first = 10, $witharea = 210) {

   my \h = sort gather
       for 1 .. $maxside -> $c {
           for 1 .. $c -> $b {
               for $c - $b + 1 .. $b -> $a {
                   if primitive-heronian-area($a,$b,$c) -> $area {
                       take [$area, $a+$b+$c, $c, $b, $a];
                   }
               }
           }
       }
   say "Primitive Heronian triangles with sides up to $maxside: ", +h;
   say "\nFirst $first:";
   show h[^$first];
   say "\nArea $witharea:";
   show h.grep: *[0] == $witharea;

}</lang>

Output:
Primitive Heronian triangles with sides up to 200: 517

First 10:
   Area Perimeter   Sides
     6     12        3×4×5
    12     16        5×5×6
    12     18        5×5×8
    24     32      4×13×15
    30     30      5×12×13
    36     36      9×10×17
    36     54      3×25×26
    42     42      7×15×20
    60     36     10×13×13
    60     40      8×15×17

Area 210:
   Area Perimeter   Sides
   210     70     17×25×28
   210     70     20×21×29
   210     84     12×35×37
   210     84     17×28×39
   210    140      7×65×68
   210    300    3×148×149

PowerShell

<lang powershell> function Get-Gcd($a, $b){

   if($a -ge $b){
       $dividend = $a
       $divisor = $b
   }
   else{
       $dividend = $b
       $divisor = $a
   }
   $leftover = 1
   while($leftover -ne 0){
       $leftover = $dividend % $divisor
       if($leftover -ne 0){

$dividend = $divisor $divisor = $leftover }

   }
   $divisor

} function Is-Heron($heronArea){

   $heronArea -gt 0 -and $heronArea % 1 -eq 0

} function Get-HeronArea($a, $b, $c){

   $s = ($a + $b + $c) / 2
   [math]::Sqrt($s * ($s - $a) * ($s - $b) * ($s - $c)) 

} $result = @() foreach ($c in 1..200){

   for($b = 1; $b -le $c; $b++){
       for($a = 1; $a -le $b; $a++){
           if((Get-Gcd $c (Get-Gcd $b $a)) -eq 1 -and (Is-Heron(Get-HeronArea $a $b $c))){
               $result += @(,@($a, $b, $c,($a + $b + $c), (Get-HeronArea $a $b $c)))                
           }
       }
   }

} $result = $result | sort-object @{Expression={$_[4]}}, @{Expression={$_[3]}}, @{Expression={$_[2]}} "Primitive Heronian triangles with sides up to 200: $($result.length)`nFirst ten when ordered by increasing area, then perimeter,then maximum sides:`nSides`t`t`t`tPerimeter`tArea" for($i = 0; $i -lt 10; $i++){ "$($result[$i][0])`t$($result[$i][1])`t$($result[$i][2])`t`t`t$($result[$i][3])`t`t`t$($result[$i][4])" } "`nArea = 210`nSides`t`t`t`tPerimeter`tArea" foreach($i in $result){

   if($i[4] -eq 210){
       "$($i[0])`t$($i[1])`t$($i[2])`t`t`t$($i[3])`t`t`t$($i[4])"
   } 

} </lang>

Output:

<lang> Primitive Heronian triangles with sides up to 200: 517

First ten when ordered by increasing area, then perimeter,then maximum sides: Sides Perimeter Area 3 4 5 12 6 5 5 6 16 12 5 5 8 18 12 4 13 15 32 24 5 12 13 30 30 9 10 17 36 36 3 25 26 54 36 7 15 20 42 42 10 13 13 36 60 8 15 17 40 60

Area = 210 Sides Perimeter Area 17 25 28 70 210 20 21 29 70 210 12 35 37 84 210 17 28 39 84 210 7 65 68 140 210 3 148 149 300 210 </lang>

Python

<lang python>from math import sqrt from fractions import gcd from itertools import product


def hero(a, b, c):

   s = (a + b + c) / 2
   a2 = s*(s-a)*(s-b)*(s-c)
   return sqrt(a2) if a2 > 0 else 0
   
   

def is_heronian(a, b, c):

   a = hero(a, b, c)
   return a > 0 and a.is_integer()
   

def gcd3(x, y, z):

   return gcd(gcd(x, y), z)


if __name__ == '__main__':

   maxside = 200
   h = [(a, b, c) for a,b,c in product(range(1, maxside + 1), repeat=3) 
        if a <= b <= c and a + b > c and gcd3(a, b, c) == 1 and is_heronian(a, b, c)]
   h.sort(key = lambda x: (hero(*x), sum(x), x[::-1]))   # By increasing area, perimeter, then sides
   print('Primitive Heronian triangles with sides up to %i:' % maxside, len(h))
   print('\nFirst ten when ordered by increasing area, then perimeter,then maximum sides:')
   print('\n'.join('  %14r perim: %3i area: %i' 
                   % (sides, sum(sides), hero(*sides)) for sides in h[:10]))
   print('\nAll with area 210 subject to the previous ordering:')
   print('\n'.join('  %14r perim: %3i area: %i' 
                   % (sides, sum(sides), hero(*sides)) for sides in h
                   if hero(*sides) == 210))</lang>
Output:
Primitive Heronian triangles with sides up to 200: 517

First ten when ordered by increasing area, then perimeter,then maximum sides:
       (3, 4, 5) perim:  12 area: 6
       (5, 5, 6) perim:  16 area: 12
       (5, 5, 8) perim:  18 area: 12
     (4, 13, 15) perim:  32 area: 24
     (5, 12, 13) perim:  30 area: 30
     (9, 10, 17) perim:  36 area: 36
     (3, 25, 26) perim:  54 area: 36
     (7, 15, 20) perim:  42 area: 42
    (10, 13, 13) perim:  36 area: 60
     (8, 15, 17) perim:  40 area: 60

All with area 210 subject to the previous ordering:
    (17, 25, 28) perim:  70 area: 210
    (20, 21, 29) perim:  70 area: 210
    (12, 35, 37) perim:  84 area: 210
    (17, 28, 39) perim:  84 area: 210
     (7, 65, 68) perim: 140 area: 210
   (3, 148, 149) perim: 300 area: 210

R

Mostly adopted from Python implementation:

<lang R> area <- function(a, b, c) {

   s = (a + b + c) / 2
   a2 = s*(s-a)*(s-b)*(s-c)
   if (a2>0) sqrt(a2) else 0

}

is.heronian <- function(a, b, c) {

   h = area(a, b, c)
   h > 0 && 0==h%%1

}

  1. borrowed from stackoverflow http://stackoverflow.com/questions/21502181/finding-the-gcd-without-looping-r

gcd <- function(x,y) {

 r <- x%%y;
 ifelse(r, gcd(y, r), y)

}

gcd3 <- function(x, y, z) {

   gcd(gcd(x, y), z)

}

maxside = 200 r <- NULL for(c in 1:maxside){

   for(b in 1:c){
       for(a in 1:b){
           if(1==gcd3(a, b, c) && is.heronian(a, b, c)) {
               r <- rbind(r,c(a=a, b=b, c=c, perimeter=a+b+c, area=area(a,b,c)))
           }
       }
   }

}

cat("There are ",nrow(r)," Heronian triangles up to a maximal side length of ",maxside,".\n", sep="") cat("Showing the first ten ordered first by perimeter, then by area:\n") print(head(r[order(x=r[,"perimeter"],y=r[,"area"]),],n=10)) </lang>

Output:

<lang>There are 517 Heronian triangles up to a maximal side length of 200. Showing the first ten ordered first by perimeter, then by area:

      a  b  c perimeter area
[1,]  3  4  5        12    6
[2,]  5  5  6        16   12
[3,]  5  5  8        18   12
[4,]  5 12 13        30   30
[5,]  4 13 15        32   24
[6,]  9 10 17        36   36
[7,] 10 13 13        36   60
[8,]  8 15 17        40   60
[9,]  7 15 20        42   42

[10,] 13 14 15 42 84</lang>

Racket

<lang>#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)))
       }))</lang>

This program generates HTML, so the output is inline with the page, not in a <pre> block.

Output:

number of primitive triangles found with perimeter ≤ 200 = 517

SidesPerimeterArea
" + list[i][0] + " x " + list[i][1] + " x " + list[i][2] + "" + list[i][3] + "" + list[i][4] + "
#sidesperimeterarea
13,4,5126
25,5,61612
35,5,81812
44,13,153224
55,12,133030
69,10,173636
73,25,265436
87,15,204242
910,13,133660
108,15,174060
#sidesperimeterarea
117,25,2870210
220,21,2970210
312,35,3784210
417,28,3984210
57,65,68140210
63,148,149300210

REXX

This REXX version makes use of these facts:

  •   if   A   is even,   then   B   and   C   must be odd.
  •   if   B   is even,   then   C   must be odd.
  •   if   A   and   B   are odd,   then   C   must be even.
  •   with the above truisms, then   C   can be incremented by   2.

Programming notes:

The   hGCD   subroutine is a specialized version of a GCD routine in that it doesn't check for non-positive integers;   it also expects 3 arguments.

Also, a fair amount of code was added to optimize the speed   (at the expense of program simplicity);   by thoughtful ordering of
the "elimination" checks, and also the use of an integer version of a   SQRT   subroutine, the execution time was greatly reduced
(by a factor of eight).   Note that the   hIsqrt   (heronian Integer sqare root)   subroutine doesn't use floating point.
[hIsqrt   is a modified version of an   Isqrt   function.]

This REXX version doesn't need to explicitly sort the triangles. <lang rexx>/*REXX program generates primitive Heronian triangles by side length and area.*/ parse arg N first area . /*get optional arguments from C.L.*/ if N== | N==',' then N=200 /*Not specified? Then use default.*/ if first== | first==',' then first= 10 /* " " " " " */ if area== | area==',' then area=210 /* " " " " " */ numeric digits 99; numeric digits max(9, 1+length(N**5)) /*ensure 'nuff digs.*/ call Heron; HT='Heronian triangles' /*invoke the Heron subroutine. */ say # ' primitive' HT "found with sides up to " N ' (inclusive).' call show , 'listing of the first ' first ' primitive' HT":" call show area, 'listing of the (above) found primitive' HT "with an area of " area exit /*stick a fork in it, we're all done. */ /*────────────────────────────────────────────────────────────────────────────*/ Heron: @.=0; #=0; minP=9e9; maxP=0; maxA=0; minA=9e9; Ln=length(N) /* _ */

                     #.=0; #.2=1  #.3=1;  #.7=1;  #.8=1    /*digits ¬good √  */
 do     a=3  to N                     /*start at a minimum side length of 3. */
 odd=a//2;   ev=\odd                  /*if  A  is even,  B and C must be odd.*/
   do   b=a+ev  to N  by 1+ev;     ab=a+b           /*AB:  is a shortcut sum.*/
   if b//2==0  then                bump=1           /*B  is even? Then C≡odd.*/
               else  if  odd  then bump=1           /*if A and B odd, C≡even.*/
                              else bump=0           /*OK,  biz is as usual.  */
     do c=b+bump  to N  by 2;   s=(ab+c)/2          /*calculate Perimeter, S.*/
     _=s*(s-a)*(s-b)*(s-c);  if _<=0   then iterate /*_ isn't positive, skip.*/
     parse var _ '.'   z  ;  if z\== then iterate /*not an integer,   skip.*/
     parse var _  -1 z  ;  if #.z    then iterate /*last dig ¬square, skip.*/
     ar=hIsqrt(_);        if ar*ar\==_ then iterate /*area ¬ an integer,skip.*/
     if hGCD(a,b,c)\==1                then iterate /*GCD of sides ¬ 1, skip.*/
     #=#+1;      p=ab+c                             /*primitive Heron triang.*/
     minP=min( p,minP);   maxP=max( p,maxP);     Lp=length(maxP)
     minA=min(ar,minA);   maxA=max(ar,maxA);     La=length(maxA);      @.ar=
     _=@.ar.p.0+1                                   /*bump triangle counter. */
     @.ar.p.0=_;  @.ar.p._=right(a,Ln)  right(b,Ln)  right(c,Ln)    /*unique.*/
     end   /*c*/                      /* [↑]  keep each unique perimeter #.  */
   end     /*b*/
 end       /*a*/

return # /*return number of Heronian triangles. */ /*────────────────────────────────────────────────────────────────────────────*/ hIsqrt: procedure; parse arg x; q=1;r=0; do while q<=x; q=q*4; end; do while q>1

 q=q%4; _=x-r-q; r=r%2; if _>=0 then parse value _ r+q with x r; end;  return r

/*────────────────────────────────────────────────────────────────────────────*/ show: m=0; say; say; parse arg ae; say arg(2); if ae\== then first=9e9 say; $=left(,9); $a=$"area:"; $p=$'perimeter:'; $s=$"sides:" /*literals*/

     do   i=minA  to maxA; if ae\== & i\==ae  then iterate       /*= area? */
       do j=minP  to maxP  until m>=first  /*only display the  FIRST entries.*/
         do k=1  for @.i.j.0;   m=m+1      /*display each  perimeter  entry. */
         say right(m,9)    $a   right(i,La)    $p   right(j,Lp)    $s   @.i.j.k
         end   /*k*/
       end     /*j*/                       /* [↑]  use the known perimeters. */
     end       /*i*/                       /* [↑]  show any found triangles. */

return /*────────────────────────────────────────────────────────────────────────────────────────────────────────────────*/ hGCD: procedure; parse arg x; do j=2 for 2; y=arg(j); do until y==0; parse value x//y y with y x; end; end; return x</lang>

Output:
517  primitive Heronian triangles found with sides up to  200  (inclusive).


listing of the first  10  primitive Heronian triangles:

        1          area:     6          perimeter:  12          sides:   3   4   5
        2          area:    12          perimeter:  16          sides:   5   5   6
        3          area:    12          perimeter:  18          sides:   5   5   8
        4          area:    24          perimeter:  32          sides:   4  13  15
        5          area:    30          perimeter:  30          sides:   5  12  13
        6          area:    36          perimeter:  36          sides:   9  10  17
        7          area:    36          perimeter:  54          sides:   3  25  26
        8          area:    42          perimeter:  42          sides:   7  15  20
        9          area:    60          perimeter:  36          sides:  10  13  13
       10          area:    60          perimeter:  40          sides:   8  15  17


listing of the (above) found primitive Heronian triangles with an area of  210

        1          area:   210          perimeter:  70          sides:  17  25  28
        2          area:   210          perimeter:  70          sides:  20  21  29
        3          area:   210          perimeter:  84          sides:  12  35  37
        4          area:   210          perimeter:  84          sides:  17  28  39
        5          area:   210          perimeter: 140          sides:   7  65  68
        6          area:   210          perimeter: 300          sides:   3 148 149

Ruby

<lang ruby>class Triangle

 def self.valid?(a,b,c)      # class method
   short, middle, long = [a, b, c].sort
   short + middle > long
 end
 
 attr_reader :sides, :perimeter, :area
 
 def initialize(a,b,c)
   @sides = [a, b, c].sort
   @perimeter = a + b + c
   s = @perimeter / 2.0
   @area = Math.sqrt(s * (s - a) * (s - b) * (s - c))
 end
 
 def heronian?
   area == area.to_i
 end
 
 def <=>(other)
   [area, perimeter, sides] <=> [other.area, other.perimeter, other.sides]
 end
 
 def to_s
   "%-11s%6d%8.1f" % [sides.join('x'), perimeter, area]
 end

end

max, area = 200, 210 prim_triangles = [] 1.upto(max) do |a|

 a.upto(max) do |b|
   b.upto(max) do |c|
     next if a.gcd(b).gcd(c) > 1
     prim_triangles << Triangle.new(a, b, c) if Triangle.valid?(a, b, c)
   end
 end

end

sorted = prim_triangles.select(&:heronian?).sort

puts "Primitive heronian triangles with sides upto #{max}: #{sorted.size}" puts "\nsides perim. area" puts sorted.first(10).map(&:to_s) puts "\nTriangles with an area of: #{area}" sorted.each{|tr| puts tr if tr.area == area}</lang>

Output:
Primitive heronian triangles with sides upto 200: 517

sides       perim.   area
3x4x5          12     6.0
5x5x6          16    12.0
5x5x8          18    12.0
4x13x15        32    24.0
5x12x13        30    30.0
9x10x17        36    36.0
3x25x26        54    36.0
7x15x20        42    42.0
10x13x13       36    60.0
8x15x17        40    60.0

Triangles with an area of: 210
17x25x28       70   210.0
20x21x29       70   210.0
12x35x37       84   210.0
17x28x39       84   210.0
7x65x68       140   210.0
3x148x149     300   210.0

Scala

<lang scala>object Heron extends scala.collection.mutable.MutableList[Seq[Int]] with App {

   private final val n = 200
   for (c <- 1 to n; b <- 1 to c; a <- 1 to b if gcd(gcd(a, b), c) == 1) {
       val p = a + b + c
       val s = p / 2D
       val area = Math.sqrt(s * (s - a) * (s - b) * (s - c))
       if (isHeron(area))
           appendElem(Seq(a, b, c, p, area.toInt))
   }
   print(s"Number of primitive Heronian triangles with sides up to $n: " + length)
   private final val list = sortBy(i => (i(4), i(3)))
   print("\n\nFirst ten when ordered by increasing area, then perimeter:" + header)
   list slice (0, 10) map format foreach print
   print("\n\nArea = 210" + header)
   list filter { _(4) == 210 } map format foreach print
   private def gcd(a: Int, b: Int) = {
       var leftover = 1
       var (dividend, divisor) = if (a > b) (a, b) else (b, a)
       while (leftover != 0) {
           leftover = dividend % divisor
           if (leftover > 0) {
               dividend = divisor
               divisor = leftover
           }
       }
       divisor
   }
   private def isHeron(h: Double) = h % 1 == 0 && h > 0
   private final val header = "\nSides           Perimeter   Area"
   private def format: Seq[Int] => String = "\n%3d x %3d x %3d %5d %10d".format

}</lang>

Swift

Works with Swift 1.2 <lang Swift>import Foundation

typealias PrimitiveHeronianTriangle = (s1:Int, s2:Int, s3:Int, p:Int, a:Int)

func heronianArea(side1 s1:Int, side2 s2:Int, side3 s3:Int) -> Int? {

   let d1 = Double(s1)
   let d2 = Double(s2)
   let d3 = Double(s3)
   
   let s = (d1 + d2 + d3) / 2.0
   let a = sqrt(s * (s - d1) * (s - d2) * (s - d3))
   
   if a % 1 != 0 || a <= 0 {
       return nil
   } else {
       return Int(a)
   }

}

func gcd(a:Int, b:Int) -> Int {

   if b != 0 {
       return gcd(b, a % b)
   } else {
       return abs(a)
   }

}

var triangles = [PrimitiveHeronianTriangle]()

for s1 in 1...200 {

   for s2 in 1...s1 {
       for s3 in 1...s2 {
           if gcd(s1, gcd(s2, s3)) == 1, let a = heronianArea(side1: s1, side2: s2, side3: s3) {
               triangles.append((s3, s2, s1, s1 + s2 + s3, a))
           }
       }
   }

}

sort(&triangles) {t1, t2 in

   if t1.a < t2.a || t1.a == t2.a && t1.p < t2.p {
       return true
   } else {
       return false
   }

}

println("Number of primitive Heronian triangles with sides up to 200: \(triangles.count)\n") println("First ten sorted by area, then perimeter, then maximum side:\n") println("Sides\t\t\tPerimeter\tArea")

for t in triangles[0...9] {

   println("\(t.s1)\t\(t.s2)\t\(t.s3)\t\t\(t.p)\t\t\(t.a)")

}</lang>

Output:
Number of primitive Heronian triangles with sides up to 200: 517

First ten sorted by area, then perimeter, then maximum side:

Sides			Perimeter	Area
3	4	5		12		6
5	5	6		16		12
5	5	8		18		12
4	13	15		32		24
5	12	13		30		30
9	10	17		36		36
3	25	26		54		36
7	15	20		42		42
10	13	13		36		60
8	15	17		40		60

Tcl

<lang tcl> if {[info commands let] eq ""} {

   #make some math look nicer:
   proc let {name args} {
       tailcall ::set $name [uplevel 1 $args]
   }
   interp alias {} = {} expr
   namespace import ::tcl::mathfunc::* ::tcl::mathop::*
   interp alias {} sum {} +
   # a simple adaptation of gcd from http://wiki.tcl.tk/2891
   proc coprime {a args} {
       set gcd $a
       foreach arg $args {
           while {$arg != 0} {
               set t $arg
               let arg = $gcd % $arg
               set gcd $t
               if {$gcd == 1} {return true}
           }
       }
       return false
   }

}

namespace eval Hero {

   # Integer square root:  returns 0 if n is not a square.
   proc isqrt? {n} {
       let r = entier(sqrt($n))
       if {$r**2 == $n} {
           return $r
       } else {
           return 0
       }
   }
   # The square of a triangle's area
   proc squarea {a b c} {
       let s = ($a + $b + $c) / 2.0
       let sqrA = $s * ($s - $a) * ($s - $b) * ($s - $c)
       return $sqrA
   }
   proc is_heronian {a b c} {
       isqrt? [squarea $a $b $c]
   }
   proc primitive_triangles {db max} {
       for {set a 1} {$a <= $max} {incr a} {
           for {set b $a} {$b <= $max} {incr b} {
               let maxc = min($a+$b,$max)
               for {set c $b} {$c <= $maxc} {incr c} {
                   set area [is_heronian $a $b $c]
                   if {$area && [coprime $a $b $c]} {
                       set perimiter [expr {$a + $b + $c}]
                       $db eval {insert into herons (area, perimiter, a, b, c) values ($area, $perimiter, $a, $b, $c)}
                   }
               }
           }
       }
   }

}

proc main {db} {

   $db eval {create table herons (area int, perimiter int, a int, b int, c int)}
   set max 200
   puts "Calculating Primitive Heronian triangles up to size length $max"
   puts \t[time {Hero::primitive_triangles $db $max} 1]
   puts "Total Primitive Heronian triangles with side lengths <= $max:"
   $db eval {select count(1) count from herons} {
       puts "\t$count"
   }
   puts "First ten when ordered by increasing area, perimiter, max side length:"
   $db eval {select * from herons order by area, perimiter, c limit 10} {
       puts "\t($a, $b, $c)  perimiter = $perimiter;  area = $area"
   }
   puts "All of area 210:"
   $db eval {select * from herons where area=210 order by area, perimiter, c} {
       puts "\t($a, $b, $c)  perimiter = $perimiter;  area = $area"
   }

}


package require sqlite3 sqlite3 db :memory: main db </lang>

Output:
Calculating Primitive Heronian triangles up to size length 200
        11530549 microseconds per iteration
Total Primitive Heronian triangles with side lengths <= 200:
        517
First ten when ordered by increasing area, perimiter, max side length:
        (3, 4, 5)  perimiter = 12;  area = 6
        (5, 5, 6)  perimiter = 16;  area = 12
        (5, 5, 8)  perimiter = 18;  area = 12
        (4, 13, 15)  perimiter = 32;  area = 24
        (5, 12, 13)  perimiter = 30;  area = 30
        (9, 10, 17)  perimiter = 36;  area = 36
        (3, 25, 26)  perimiter = 54;  area = 36
        (7, 15, 20)  perimiter = 42;  area = 42
        (10, 13, 13)  perimiter = 36;  area = 60
        (8, 15, 17)  perimiter = 40;  area = 60
All of area 210:
        (17, 25, 28)  perimiter = 70;  area = 210
        (20, 21, 29)  perimiter = 70;  area = 210
        (12, 35, 37)  perimiter = 84;  area = 210
        (17, 28, 39)  perimiter = 84;  area = 210
        (7, 65, 68)  perimiter = 140;  area = 210
        (3, 148, 149)  perimiter = 300;  area = 210

zkl

Translation of: Python

<lang zkl>fcn hero(a,b,c){ //--> area (float)

  s,a2:=(a + b + c).toFloat()/2, s*(s - a)*(s - b)*(s - c);
  (a2 > 0) and a2.sqrt() or 0.0

} fcn isHeronian(a,b,c){

  A:=hero(a,b,c);
  (A>0) and A.modf()[1].closeTo(0.0,1.0e-6) and A  //--> area or False

}</lang> <lang zkl>const MAX_SIDE=200; heros:=Sink(List); foreach a,b,c in ([1..MAX_SIDE],[a..MAX_SIDE],[b..MAX_SIDE]){

  if(a.gcd(b).gcd(c)==1 and (h:=isHeronian(a,b,c))) heros.write(T(h,a+b+c,a,b,c));

} // sort by increasing area, perimeter, then sides heros=heros.close().sort(fcn([(h1,p1,_,_,c1)],[(h2,p2,_,_,c2)]){

  if(h1!=h2) return(h1<h2);
  if(p1!=p2) return(p1<p2);
  c1<c2;

});

println("Primitive Heronian triangles with sides up to %d: ".fmt(MAX_SIDE),heros.len());

println("First ten when ordered by increasing area, then perimeter,then maximum sides:"); println("Area Perimeter Sides"); heros[0,10].pump(fcn(phabc){ "%3s %8d %3dx%dx%d".fmt(phabc.xplode()).println() });

println("\nAll with area 210 subject to the previous ordering:"); println("Area Perimeter Sides"); heros.filter(fcn([(h,_)]){ h==210 })

 .pump(fcn(phabc){ "%3s %8d %3dx%dx%d".fmt(phabc.xplode()).println() });</lang>
Output:
Primitive Heronian triangles with sides up to 200: 517
First ten when ordered by increasing area, then perimeter,then maximum sides:
Area Perimeter Sides
  6       12   3x4x5
 12       16   5x5x6
 12       18   5x5x8
 24       32   4x13x15
 30       30   5x12x13
 36       36   9x10x17
 36       54   3x25x26
 42       42   7x15x20
 60       36  10x13x13
 60       40   8x15x17

All with area 210 subject to the previous ordering:
Area Perimeter Sides
210       70  17x25x28
210       70  20x21x29
210       84  12x35x37
210       84  17x28x39
210      140   7x65x68
210      300   3x148x149