Task: Heronian triangles


C++: #include <algorithm>
#include <cmath>
#include <iostream>
#include <tuple>
#include <vector>

int gcd(int a, int b)
{
    int rem = 1, dividend, divisor;
    std::tie(divisor, dividend) = std::minmax(a, b);
    while (rem != 0) {
        rem = dividend % divisor;
        if (rem != 0) {
            dividend = divisor;
            divisor = rem;
        }
    }
    return divisor;
}

struct Triangle
{
    int a;
    int b;
    int c;
};

int perimeter(const Triangle& triangle)
{
    return triangle.a + triangle.b + triangle.c;
}

double area(const Triangle& t)
{
    double p_2 = perimeter(t) / 2.;
    double area_sq = p_2 * ( p_2 - t.a ) * ( p_2 - t.b ) * ( p_2 - t.c );
    return sqrt(area_sq);
}

std::vector<Triangle> generate_triangles(int side_limit = 200)
{
    std::vector<Triangle> result;
    for(int a = 1; a <= side_limit; ++a)
        for(int b = 1; b <= a; ++b)
            for(int c = a+1-b; c <= b; ++c) 
            {
                Triangle t{a, b, c};
                double t_area = area(t);
                if(t_area == 0) continue;
                if( std::floor(t_area) == std::ceil(t_area) && gcd(a, gcd(b, c)) == 1)
                    result.push_back(t);
            }
    return result;
}

bool compare(const Triangle& lhs, const Triangle& rhs)
{
    return std::make_tuple(area(lhs), perimeter(lhs), std::max(lhs.a, std::max(lhs.b, lhs.c))) <
           std::make_tuple(area(rhs), perimeter(rhs), std::max(rhs.a, std::max(rhs.b, rhs.c)));
}

struct area_compare
{
    bool operator()(const Triangle& t, int i) { return area(t) < i; }
    bool operator()(int i, const Triangle& t) { return i < area(t); }
};

int main()
{
    auto tri = generate_triangles();
    std::cout << "There are " << tri.size() << " primitive Heronian triangles with sides up to 200\n\n";

    std::cout << "First ten when ordered by increasing area, then perimeter, then maximum sides:\n";
    std::sort(tri.begin(), tri.end(), compare);
    std::cout << "area\tperimeter\tsides\n";
    for(int i = 0; i < 10; ++i)
        std::cout << area(tri[i]) << '\t' << perimeter(tri[i]) << "\t\t" <<
                     tri[i].a << 'x' << tri[i].b << 'x' << tri[i].c << '\n';

    std::cout << "\nAll with area 210 subject to the previous ordering:\n";
    auto range = std::equal_range(tri.begin(), tri.end(), 210, area_compare());
    std::cout << "area\tperimeter\tsides\n";
    for(auto it = range.first; it != range.second; ++it)
        std::cout << area(*it) << '\t' << perimeter(*it) << "\t\t" <<
                     it->a << 'x' << it->b << 'x' << it->c << '\n';
}









Fortran:  
      MODULE GREEK MATHEMATICIANS	
       CONTAINS
        INTEGER FUNCTION GCD(I,J)	
         INTEGER I,J	
         INTEGER N,M,R	
          N = MAX(I,J)	
          M = MIN(I,J)	
    1     R = MOD(N,M)		
c          write (6,*) "M,N,R",M,N,R
          IF (R.GT.0) THEN	
            N = M			
            M = R			
            IF (R .GT. 1) GO TO 1	
          END IF			
          GCD = M			
        END FUNCTION GCD	
        FUNCTION GCD3(I,J,K)	
         INTEGER I,J,K	
         INTEGER R	
          R = GCD(I,J)		
          IF (R .GT. 1) R = GCD(R,K)	
          GCD3 = R		
        END FUNCTION GCD3

        REAL*8 FUNCTION HERO(SIDE)	
Calculations could proceed with non-integer sides.
         INTEGER SIDE(3)	
         REAL*8 S		
          S = SUM(SIDE)		
          S = S/2		
          S = S*PRODUCT(S - SIDE)	
          HERO = SIGN(SQRT(ABS(S)),S)	
        END FUNCTION HERO		
      END MODULE GREEK MATHEMATICIANS	

      PROGRAM TEST		
      USE GREEK MATHEMATICIANS	
      INTEGER LIMIT,LOTS	
      PARAMETER (LIMIT = 200, LOTS = 666)	
      INTEGER I,J,K,SIDE(3)	
      EQUIVALENCE (SIDE(1),I),(SIDE(2),J),(SIDE(3),K)	
      REAL*8 A			
      TYPE ABLOB		
       INTEGER SIDE(3)		
       INTEGER PERIMETER	
       INTEGER AREA		
      END TYPE ABLOB		
      TYPE(ABLOB) STASH(LOTS)	
      INTEGER N,XNDX(LOTS)	
      INTEGER H,T		
      LOGICAL CURSE		
      INTEGER TASTE,CHOICE	
      PARAMETER (TASTE = 10, CHOICE = 210)	

Collect some triangles.
      N = 0	
      DO K = 1,LIMIT	
        DO J = 1,K		
          DO I = 1,J			
            IF (GCD3(I,J,K).GT.1) CYCLE	
            A = HERO(SIDE)		
            IF (A.LE.0) CYCLE		
            IF (A .NE. INT(A)) CYCLE	
            N = N + 1			
            IF (N.GT.LOTS) STOP "Too many
            XNDX(N) = N			
            STASH(N).SIDE = SIDE		
            STASH(N).PERIMETER = SUM(SIDE)	
            STASH(N).AREA = A			
c            WRITE (6,10) N,STASH(N)
   10       FORMAT (I4,":",3I4,I7,I8)	
          END DO
        END DO
      END DO
      WRITE (6,11) N,LIMIT	
   11 FORMAT (I0," triangles of integral area. Sides up to ",I0)

Comb sort involves coding only one test, and the comparison is to be compound...
      H = N - 1	
   20 H = MAX(1,H*10/13)	
      IF (H.EQ.9 .OR. H.EQ.10) H = 11	
      CURSE = .FALSE.		
      DO 24 I = N - H,1,-1	
        IF (STASH(XNDX(I)).AREA - STASH(XNDX(I + H)).AREA) 24,21,23 	
   21   IF (STASH(XNDX(I)).PERIMETER-STASH(XNDX(I+H)).PERIMETER)24,22,23 	
   22   IF (MAXVAL(STASH(XNDX(I)).SIDE)			
     1    - MAXVAL(STASH(XNDX(I+H)).SIDE)) 24,24,23	
   23     T=XNDX(I); XNDX(I)=XNDX(I+H); XNDX(I+H)=T	
          CURSE = .TRUE.			
   24 CONTINUE				
      IF (CURSE .OR. H.GT.1) GO TO 20	

Cast forth the results, as per the specification.
      WRITE (6,30) TASTE
   30 FORMAT ("First ",I0,", ordered by area, perimeter, longest side.",
     1 /,"Index ---Sides--- Perimeter Area")
      DO I = 1,TASTE
        WRITE (6,10) XNDX(I),STASH(XNDX(I))
      END DO

      WRITE (6,31) CHOICE
   31 FORMAT ("Those triangles with area",I7)
      DO I = 1,N	
        IF (STASH(XNDX(I)).AREA.NE.CHOICE) CYCLE
        WRITE (6,10) XNDX(I),STASH(XNDX(I))	
      END DO		
      END