Task: Law of cosines - triples







Python: N = 13

def method1(N=N):
    squares = [x**2 for x in range(0, N+1)]
    sqrset = set(squares)
    tri90, tri60, tri120 = (set() for _ in range(3))
    for a in range(1, N+1):
        a2 = squares[a]
        for b in range(1, a + 1):
            b2 = squares[b]
            c2 = a2 + b2
            if c2 in sqrset:
                tri90.add(tuple(sorted((a, b, int(c2**0.5)))))
            ab = a * b
            c2 -= ab
            if c2 in sqrset:
                tri60.add(tuple(sorted((a, b, int(c2**0.5)))))
            c2 += 2 * ab
            if c2 in sqrset:
                tri120.add(tuple(sorted((a, b, int(c2**0.5)))))
    return  sorted(tri90), sorted(tri60), sorted(tri120)

if __name__ == '__main__':
    print(f'Integer triangular triples for sides 1..{N}:')
    for angle, triples in zip([90, 60, 120], method1(N)):
        print(f'  {angle:3}° has {len(triples)} solutions:\n    {triples}')
    _, t60, _ = method1(10_000)
    notsame = sum(1 for a, b, c in t60 if a != b or b != c)
    print('Extra credit:', notsame)




Fortran: MODULE LAW_OF_COSINES
    IMPLICIT NONE

    CONTAINS

    
    REAL FUNCTION COSINE_SIDE(SIDE_A, SIDE_B, ANGLE)
        INTEGER, INTENT(IN) :: SIDE_A, SIDE_B
        REAL(8), INTENT(IN) :: ANGLE
        
        COSINE_SIDE = SIDE_A**2 + SIDE_B**2 - 2*SIDE_A*SIDE_B*COS(ANGLE)
        COSINE_SIDE = COSINE_SIDE**0.5
    END FUNCTION COSINE_SIDE

    
    REAL(8) FUNCTION DEG2RAD(ANGLE)
        REAL(8), INTENT(IN) :: ANGLE
        REAL(8), PARAMETER :: PI = 4.0D0*DATAN(1.D0)

        DEG2RAD = ANGLE*(PI/180)
    END FUNCTION DEG2RAD

    
    FUNCTION INT_SORTED(ARRAY) RESULT(SORTED)
        INTEGER, DIMENSION(:), INTENT(IN) :: ARRAY
        INTEGER, DIMENSION(SIZE(ARRAY)) :: SORTED, TEMP
        INTEGER :: MAX_VAL, DIVIDE
            
        SORTED = ARRAY
        TEMP = ARRAY
        DIVIDE = SIZE(ARRAY)

        DO WHILE (DIVIDE .NE. 1)
            MAX_VAL = MAXVAL(SORTED(1:DIVIDE))
            TEMP(DIVIDE) = MAX_VAL
            TEMP(MAXLOC(SORTED(1:DIVIDE))) = SORTED(DIVIDE)
            SORTED = TEMP
            DIVIDE = DIVIDE - 1
        END DO
    END FUNCTION INT_SORTED

    
    SUBROUTINE APPEND(ARRAY, ELEMENT)
        INTEGER, DIMENSION(:), ALLOCATABLE, INTENT(INOUT) :: ARRAY
        INTEGER, DIMENSION(:), ALLOCATABLE :: TEMP
        INTEGER :: ELEMENT
        INTEGER :: I, ISIZE

        IF (ALLOCATED(ARRAY)) THEN
            ISIZE = SIZE(ARRAY)
            ALLOCATE(TEMP(ISIZE+1))

            DO I=1, ISIZE
                TEMP(I) = ARRAY(I)
            END DO

            TEMP(ISIZE+1) = ELEMENT

            DEALLOCATE(ARRAY)

            CALL MOVE_ALLOC(TEMP, ARRAY)
        ELSE
            ALLOCATE(ARRAY(1))
            ARRAY(1) = ELEMENT
        END IF
        
    END SUBROUTINE APPEND

    
    LOGICAL FUNCTION CONTAINS_ARR(ARRAY, ELEMENT)
        INTEGER, DIMENSION(:), INTENT(IN) :: ARRAY
        INTEGER, DIMENSION(:) :: ELEMENT
        INTEGER, DIMENSION(SIZE(ELEMENT)) :: TEMP, SORTED_ELEMENT
        INTEGER :: I, COUNTER, J

        COUNTER = 0

        ELEMENT = INT_SORTED(ELEMENT)

        DO I=1,SIZE(ARRAY),SIZE(ELEMENT)
            TEMP = ARRAY(I:I+SIZE(ELEMENT)-1)
            DO J=1,SIZE(ELEMENT)
                IF (ELEMENT(J) .EQ. TEMP(J)) THEN
                    COUNTER = COUNTER + 1
                END IF
            END DO

            IF (COUNTER .EQ. SIZE(ELEMENT)) THEN
                CONTAINS_ARR = .TRUE.
                RETURN
            END IF
        END DO

        CONTAINS_ARR = .FALSE.
    END FUNCTION CONTAINS_ARR

    
    INTEGER FUNCTION COSINE_TRIPLES(MIN_NUM, MAX_NUM, ANGLE, PRINT_RESULTS) RESULT(COUNTER)
        INTEGER, INTENT(IN) :: MIN_NUM, MAX_NUM
        REAL(8), INTENT(IN) :: ANGLE
        LOGICAL, INTENT(IN) :: PRINT_RESULTS
        INTEGER, DIMENSION(:), ALLOCATABLE :: CANDIDATES
        INTEGER, DIMENSION(3) :: CANDIDATE
        INTEGER :: A, B
        REAL :: C

        COUNTER = 0

        DO A = MIN_NUM, MAX_NUM
            DO B = MIN_NUM, MAX_NUM
                C = COSINE_SIDE(A, B, DEG2RAD(ANGLE))
                IF (C .GT. MAX_NUM .OR. MOD(C, 1.) .NE. 0) THEN
                    CYCLE
                END IF

                CANDIDATE(1) = A
                CANDIDATE(2) = B
                CANDIDATE(3) = C
                IF (.NOT. CONTAINS_ARR(CANDIDATES, CANDIDATE)) THEN
                    COUNTER = COUNTER + 1
                    CALL APPEND(CANDIDATES, CANDIDATE(1))
                    CALL APPEND(CANDIDATES, CANDIDATE(2))
                    CALL APPEND(CANDIDATES, CANDIDATE(3))

                    IF (PRINT_RESULTS) THEN
                        WRITE(*,'(A,I0,A,I0,A,I0,A)') " (", CANDIDATE(1), ", ", CANDIDATE(2), ", ", CANDIDATE(3), ")"
                    END IF
                END IF
            END DO
        END DO
    END FUNCTION COSINE_TRIPLES
                  
END MODULE LAW_OF_COSINES






PROGRAM LOC
    USE LAW_OF_COSINES

    REAL(8), DIMENSION(3) :: TEST_ANGLES = (/90., 60., 120./)
    INTEGER :: I, COUNTER

    DO I = 1,SIZE(TEST_ANGLES)
        WRITE(*, '(F0.0, A)') TEST_ANGLES(I), " degree triangles: " 
        COUNTER = COSINE_TRIPLES(1, 13, TEST_ANGLES(I), .TRUE.)
        WRITE(*,'(A, I0)') "TOTAL: ", COUNTER
        WRITE(*,*) NEW_LINE('A')
    END DO

END PROGRAM LOC