# File: Histogram_Chapter6.py
# Purpose: Generates a histogram of continuous data
#          As completed after Ch 6
# Author: Emily G. Allen
# Date:

# Required Libraries
from graphics import *
from math import *

# ============================================================
# HELPFUL FUNCTIONS
# ============================================================

# Function: zeros
# Purpose: generates a list of zeros
# Inputs: size of the list
# Outputs: list of zeros
def zeros(size):
    list = []
    for x in range(size):
        list.append(0)

    return list

# Function: total
# Purpose: calculates a total of the values in a list
# Inputs: a list of data
# Outputs: the total
def total(data):
    t = 0
    for x in data:
        t = t + x

    return t

# ============================================================
# CREATE THE WINDOW
# ============================================================

# Function: createWindow
# Purpose: Create Window and set its coordinate system
# Inputs: length of x axis, offset of the axes from edge of window
# Outputs: the window
def createWindow(xLength, offset):
    w = GraphWin('Histogram', 640, 480)
    w.setBackground('white')
    w.setCoords(0, 0, 100  + 2 * offset, xLength + 2 * offset)
    return w

# ============================================================
# DRAW AXES AND LABELS
# ============================================================
    
# Function: drawAxes
# Purpose: Draw and Label Axes (including Ticks)
# Inputs: window, length of x-axis, offset
# Outputs: None
def drawAxes(window, xLength, offset):
    xAxis = Line(Point(offset, offset), Point(offset + xLength, offset))
    yAxis = Line(Point(offset, offset), Point(offset, offset + 100))

    xAxis.setWidth(2)
    yAxis.setWidth(2)

    xAxis.draw(window)
    yAxis.draw(window)

# Function: labelAxes
# Purpose: Label the Axes
# Inputs:
# Outputs:

# Function: drawTicks
# Purpose: draw and label tick marks
# Inputs:
# Outputs:

# Function: xTicks
# Purpose: Determine position of xTicks
# Inputs: numBins, binSize, list of data
# Outputs: a list of the midpoint of each bin to serve as a tick label

# Function: tickLabels
# Purpose: convert numeric tick position to string tick labels
# Inputs:
# Outputs:

# Function: drawTitle
# Purpose: draws the title of the chart
# Inputs:
# Outputs:

# ============================================================
# DRAW THE BAR CHART
# ============================================================
# Function:drawBar
# Purpose: Draw a single bar
# Inputs: window, height and width of bar
# Outputs: None
def drawBar(window, height, width):
    bar = Rectangle(Point(0, 0), Point(width, height))
    bar.setWidth(2)
    bar.setFill('blue')
    bar.draw(window)
    
# Function: drawHistogram
# Purpose: Draw all bars
# Inputs:
# Outputs:


# ============================================================
# CALCULATE RELATIVE DATA TO BE PLOTTED
# ============================================================

# Function: calcBinSize
# Purpose: Calculate Bin Size
# Inputs: data and the number of bins
# Outputs: the size of a bin
def calcBinSize(data, numBins):
    data.sort()
    minData = data[0]
    maxData = data[len(data) - 1]

    return ceil((maxData - minData) / float(numBins))

# alternative version for those that update code to work
# with math library

#def calcBinSize(data, numBins):
#    return ceil((max(data) - min(data)) / float(numBins))

# Function: calcFreq
# Purpose: Convert Raw data to Frequency Data
# Inputs: list of raw data (observations), the bin size, and the number of bins
def calcFreq(data, binSize, numBins):
    binMin = min(data) # first bin min
   
    freq = zeros(numBins) # create a list of zeros for starting counters
    for bin in range(numBins): # iterate through the bins
        binMax = binMin + binSize # set bin max

        for value in data:  # iterate through data
            # if value is in bin, add one to counter for bin
            if value >= binMin and value < binMax:
                freq[bin] = freq[bin] + 1
            
        binMin = binMax # update bin min to the old bin max
        
    return freq # return frequency data

# Function: calcRelFreq
# Purpose: Convert Frequency Data to Relative Frequency Data
# Inputs: Frequency Data
# Outputs: Relative Frequency Data
def calcRelFreq(freq):
    t = total(freq)
    # t = sum(freq) # also ok
    rfreq = []
    for x in freq:
        rfreq = rfreq + [x / float(t) * 100.0]

    return rfreq
    

# ============================================================
# IMPORT/EXPORT DATA
# ============================================================

# Function: importData
# Purpose: Import raw data from a file
# Inputs: none
# Outputs: list of raw data


# Function: exportResults
# Purpose: Save Frequency Table to a File
# Inputs: tick labels, frequency data, relative frequency data
# Outputs: 

# ============================================================
# IMAIN PROGRAM
# ============================================================
def main():

    # data set
    # data = [64, 73, 12, 84, 4, 98, 87, 6, 13, 29, 96, 10, 18, 16, 75, 43, 74, 41, 87, 90, 54, 33, 9, 38, 54, 11, 11, 13, 19, 14, 11, 36, 2, 9, 91, 86, 2, 61, 32, 53, 37, 92, 45, 30, 25, 75, 40, 74, 49, 39, 32, 36, 74, 29, 76, 24, 20, 80, 1, 16, 93, 63, 19, 72, 13, 88, 79, 75, 49, 47, 95, 5, 45, 12, 8, 33, 55, 25, 6, 55, 16, 25, 77, 64, 17, 80, 76, 42, 46, 2, 61, 73, 44, 11, 42, 63, 74, 9, 31, 31]

    # distance axes are offset from edge of window
    axesOffset = 20

    # prompt user for number of bins
    numBins = input('Please enter number of bins: ') 

    binSize = calcBinSize(data, numBins) # calculate bin size

    xLength = numBins * binSize # the length of the x-axis

    # Create the Window
    myWindow = createWindow(xLength, axesOffset)
    
    # Draw the Axes
    drawAxes(myWindow, xLength, axesOffset)
    
    # Calculate Frequency Data
    freqData = calcFreq(data, binSize, numBins)

    # Print Frequency Data to Screen
    # print freqData


# invoke main
main()
