#!/usr/bin/env gbs3

Class Body

  Static Private SOLAR_MASS As Float = 4 * Pi * Pi
  Private Const DAYS_PER_YEAR As Float = 365.24

  Public X As Float
  Public Y As Float
  Public Z As Float
  Public VX As Float
  Public VY As Float
  Public VZ As Float
  Public Mass As Float

  Static Public Sub Jupiter() As Body

    Dim P As New Body
    p.x = 4.84143144246472090e+00
    p.y = -1.16032004402742839e+00
    p.z = -1.03622044471123109e-01
    p.vx = 1.66007664274403694e-03 * DAYS_PER_YEAR
    p.vy = 7.69901118419740425e-03 * DAYS_PER_YEAR
    p.vz = -6.90460016972063023e-05 * DAYS_PER_YEAR
    p.mass = 9.54791938424326609e-04 * SOLAR_MASS
    return p

  End

  Static Public Sub Saturn() As Body

    Dim P As New Body
    p.x = 8.34336671824457987e+00
    p.y = 4.12479856412430479e+00
    p.z = -4.03523417114321381e-01
    p.vx = -2.76742510726862411e-03 * DAYS_PER_YEAR
    p.vy = 4.99852801234917238e-03 * DAYS_PER_YEAR
    p.vz = 2.30417297573763929e-05 * DAYS_PER_YEAR
    p.mass = 2.85885980666130812e-04 * SOLAR_MASS
    return p

  End

  Static Public Sub Uranus() As Body

    Dim P As New Body

    p.x = 1.28943695621391310e+01
    p.y = -1.51111514016986312e+01
    p.z = -2.23307578892655734e-01
    p.vx = 2.96460137564761618e-03 * DAYS_PER_YEAR
    p.vy = 2.37847173959480950e-03 * DAYS_PER_YEAR
    p.vz = -2.96589568540237556e-05 * DAYS_PER_YEAR
    p.mass = 4.36624404335156298e-05 * SOLAR_MASS
    return p

  End

  Static Public Sub Neptune() As Body

    Dim P As New Body

    p.x = 1.53796971148509165e+01
    p.y = -2.59193146099879641e+01
    p.z = 1.79258772950371181e-01
    p.vx = 2.68067772490389322e-03 * DAYS_PER_YEAR
    p.vy = 1.62824170038242295e-03 * DAYS_PER_YEAR
    p.vz = -9.51592254519715870e-05 * DAYS_PER_YEAR
    p.mass = 5.15138902046611451e-05 * SOLAR_MASS
    return p

  End

  Static Public Sub Sun() As Body

    Dim P As New Body

    p.mass = SOLAR_MASS
    return p

  End

  Public Sub OffsetMomentum(px As Float, py As Float, pz As Float) As Body

    vx = -px / SOLAR_MASS
    vy = -py / SOLAR_MASS
    vz = -pz / SOLAR_MASS

    Return Me

  End

End Class


Class NBodySystem

  Private Bodies As Body[]

  Public Sub _new()

    Dim PX, PY, PZ As Float
    Dim I As Integer

    Bodies = [ Body.Sun(), Body.Jupiter(), Body.Saturn(), Body.Uranus(), Body.Neptune() ]

    For I = 0 To Bodies.Max
      PX += Bodies[I].vx * Bodies[I].mass
      PY += Bodies[I].vy * Bodies[I].mass
      PZ += Bodies[I].vz * Bodies[I].mass
    Next

    Bodies[0].offsetMomentum(PX, PY, PZ)

  End

  Public Sub Advance(dt As Float)

    Dim I, J As Integer
    Dim iBody, jBody As Body
    Dim dx, dy, dz As Float
    Dim ix, iy, iz As Float
    Dim dSquared, fMag As Float
    Dim iMass, jMass, iMag, jMag As Float

    For I = 0 To Bodies.Max
      iBody = Bodies[I]
      iMass = iBody.mass
      ix = iBody.x
      iy = iBody.y
      iz = iBody.z

      For J = I + 1 To Bodies.Max
        jBody = Bodies[J]
        jMass = jBody.mass

        dx = ix - jBody.x
        dy = iy - jBody.y
        dz = iz - jBody.z

        dSquared = dx * dx + dy * dy + dz * dz
        fMag = dt / (dSquared * Sqr(dSquared))
        iMag = iMass * fMag
        jMag = jMass * fMag

        iBody.vx -= dx * jMag
        iBody.vy -= dy * jMag
        iBody.vz -= dz * jMag

        jBody.vx += dx * iMag
        jBody.vy += dy * iMag
        jBody.vz += dz * iMag
      Next
    Next

    For I = 0 To Bodies.Max
      iBody = Bodies[i]
      iBody.x += dt * iBody.vx
      iBody.y += dt * iBody.vy
      iBody.z += dt * iBody.vz
    Next

  End

  Public Sub Energy() As Float

    Dim dx, dy, dz, distance, E As Float
    Dim iBody, jBody As Body
    Dim I, J As Integer

    For I = 0 To Bodies.Max

      iBody = bodies[i]
      E += 0.5 * iBody.mass * (iBody.vx * iBody.vx + iBody.vy * iBody.vy + iBody.vz * iBody.vz)

      For J = I + 1 To Bodies.Max

        jBody = Bodies[J]
        dx = iBody.x - jBody.x
        dy = iBody.y - jBody.y
        dz = iBody.z - jBody.z

        distance = Sqr(dx*dx + dy*dy + dz*dz)
        E -= (iBody.mass * jBody.mass) / distance

      Next
    Next

    return E

  End

End Class


Dim S As New NBodySystem
Dim I, N As Integer

For N = 1 To 5

  Print S.Energy()

  For I = 1 To 100000
    S.Advance(0.01)
  Next

Next

Print S.Energy()

Error CStr(Jit.Time)

