ROMS/TOMS Developers

Algorithms Update Web Log

kate - June 17, 2009 @ 17:14
Binary trees- Comments (0)

We have a project with some novel challenges and I decided that one thing it needs is a balanced binary tree, something I learned about in a data structures class. It meant delving into some new-to-me sides of the Fortran standard, but Fortran 90 supports pointers which should be able to do the job. Because I’m not that familiar with Fortran pointers, I started building my tree with a short test program just to make sure I’m on the right track before including this stuff into the full ROMS code.

I started by creating a treenode object with pointers to the left and right children, plus the parent. I used them to insert objects into a binary tree, no problem. Then I started adding the code so that the tree would be balanced after each insertion, using the standard red-black algorithm that’s in any algorithms textbook. I repeat, this is meat-and-potatoes homework assignment level of algorithm, not something exotic at all. Here’s my f90 version:

      MODULE mod_tree
!
!================================================== Kate Hedstrom ======
!  Copyright (c) 2002-2009 The ROMS/TOMS Group                         !
!    Licensed under a MIT/X style license                              !
!    See License_ROMS.txt                                              !
!=======================================================================
!  Set up tree structure and functions.                                !
!=======================================================================
!
        implicit none

        type treenode
          type(treenode), pointer :: left => null()
          type(treenode), pointer :: right => null()
          type(treenode), pointer :: parent => null()
          logical :: red = .FALSE.
          integer :: eggs = 0
          real*8  :: dist
        end type treenode

        type(treenode), pointer :: tree

        PRIVATE
        PUBLIC :: init, insert

        CONTAINS

        SUBROUTINE init
          ALLOCATE(tree)
        END SUBROUTINE init

        SUBROUTINE insert(eggs, dist)
          integer, intent(in) :: eggs
          real*8, intent(in) :: dist
          type(treenode), pointer :: cur, p, x, y

          ALLOCATE(cur)
          cur % eggs = eggs
          cur % dist = dist

! Empty tree, deposit eggs at the top
          IF (.not. ASSOCIATED(tree % left)) THEN
            tree % left => cur
            cur % parent => tree
            RETURN
          END IF

! Otherwise find somewhere to put these eggs
! New nodes end up at the bottom until a rebalance
          p => tree % left

          DO
            IF (dist <= p % dist) THEN   
              IF (ASSOCIATED(p % left)) THEN
                p => p % left
                CYCLE
              ELSE
                p % left => cur
                cur % parent => p
                EXIT
              END IF
            ELSE
              IF (ASSOCIATED(p % right)) THEN
                p => p % right
                CYCLE
              ELSE
                p % right => cur
                cur % parent => p
                EXIT
              END IF
            END IF
          END DO
! Balance the thing... red-black for now, until I get smarter about
! balancing eggs.
          cur % red = .true.
          x => cur
          DO WHILE (x % parent % red)
            IF (ASSOCIATED(x % parent % parent % left, x % parent)) THEN
              IF (ASSOCIATED(x % parent % parent % right)) THEN
                y => x % parent % parent % right   ! uncle
                IF (y % red) THEN
                  x % parent % red = .false.
                  y % red = .false.
                  x % parent % parent % red = .true.
                  x => x % parent % parent
                END IF
              ELSE
                IF (ASSOCIATED(x, x % parent % right)) THEN
                  CALL rotate_left(x % parent)
                END IF
                x % parent % red = .false.
                x % parent % parent % red = .true.
                CALL rotate_right(x % parent % parent)
              END IF
            ELSE
! Must be right grandchild to get here
              IF (ASSOCIATED(x % parent % parent % left)) THEN
                y => x % parent % parent % left    ! aunt
                IF (y % red) THEN
                  x % parent % red = .false.
                  y % red = .false.
                  x % parent % parent % red = .true.
                  x => x % parent % parent
                END IF
              ELSE
                IF (ASSOCIATED(x, x % parent % left)) THEN
                  CALL rotate_right(x % parent)
                END IF
                x % parent % red = .false.
                x % parent % parent % red = .true.
                CALL rotate_left(x % parent % parent)
              END IF
            END IF
          END DO
          tree % left % red = .false.
        END SUBROUTINE insert
!
! For the rotating, I'm working from C code I found online for red-black trees,
! with reference to Introduction to Algorithms by Cormen, Leiserson,
! Rivest (Chapter 14). It makes right child of x into the parent of x.
!
        SUBROUTINE rotate_left(x)
          type(treenode), pointer :: x, y
          integer :: mine, theirs

          y => x % right

          IF (ASSOCIATED(y % left)) THEN
            x % right => y % left
            x % right % parent => x
          ELSE
            NULLIFY(x % right)
          END IF

          print *, "rotate_left before ", x % dist, x % eggs
          y % parent => x % parent
          print *, "rotate_left after ", x % dist, x % eggs

          IF (ASSOCIATED(x, x % parent % left)) THEN
            x % parent % left => y
          ELSE
            x % parent % right => y
          END IF
          y % left => x
          x % parent => y
        END SUBROUTINE rotate_left

        SUBROUTINE rotate_right(x)
          type(treenode), pointer :: x, y

          y => x % left

          IF (ASSOCIATED(y % right)) THEN
            x % left => y % right
            x % left % parent => x
          ELSE
            NULLIFY(x % left)
          END IF

          print *, "rotate_right before ", x % dist, x % eggs
          y % parent => x % parent
          print *, "rotate_right after ", x % dist, x % eggs

          IF (ASSOCIATED(x, x % parent % left)) THEN
            x % parent % left => y
          ELSE
            x % parent % right => y
          END IF
          y % right => x
          x % parent => y
        END SUBROUTINE rotate_right

      END MODULE mod_tree
      program main

      use mod_tree
      implicit none

        integer, parameter :: num = 3
        integer :: caviar(num)
        real*8  :: dist(num)
        integer :: i

      dist = (/ 21, 56, 78 /)
      caviar = 100*dist

       call init

       do i=1,num
          call insert(caviar(i), dist(i))
       end do

      end program main

I defy you to find a compiler which does the right thing for this code (or to find a bug in this code to explain this). I’ve tried gfortran, ifort, PGI, and Pathscale. A typical response:

rotate_left before 21.0000000000000 2100
rotate_left after 0.000000000000000E+000 0
forrtl: severe (174): SIGSEGV, segmentation fault occurred