def tie_break(n_diff1, n_diff2):
        theta1 = calc_bend_angle(n, n_diff1)
        theta2 = calc_bend_angle(n, n_diff2)

        if abs(theta1-target_theta) < abs(theta2-target_theta):
            return n_diff1
        return n_diff2
def print_candidate(num_segments, n_diff, num_potential_strands, n_diff_period):
    print('\n')

    theta = calc_bend_angle(num_segments*7, n_diff)
    theta_error = (360-theta)/3.6

    print str(num_potential_strands) + ' strands per well,',
    print str(num_segments) + ' segments,',
    print "{0:.2f}".format(theta_error) + '% theta error,',
    print str(num_segments*7) + ' bp long,',
    print str(n_diff) + " insertions/deletions ,",
    print str(n_diff/num_segments*n_diff_period) + ' insertions/deletions per',
    if n_diff_period == 1:
        print "segment"
    else:
        print str(n_diff_period) + ' segments'

    #calc_bend_angle(num_segments*7, n_diff, True, False, False)

    if abs(theta_error) < min_theta_error_candidate["theta_error"]:
        min_theta_error_candidate["theta_error"] = abs(theta_error)
        min_theta_error_candidate["num_segments"] = num_segments
        min_theta_error_candidate["n_diff"] = n_diff
        min_theta_error_candidate["n_diff_period"] = n_diff_period
        min_theta_error_candidate["num_potential_strands"] = num_potential_strands
def solve_for_n_diff(n, target_theta=360):
    
    tolerance = 0.01

    n_diff = 0 # num insertions deletions
    last_n_diff = -1
    n_diff_max = n
    n_diff_min = 0
    theta = 0

    def tie_break(n_diff1, n_diff2):
        theta1 = calc_bend_angle(n, n_diff1)
        theta2 = calc_bend_angle(n, n_diff2)

        if abs(theta1-target_theta) < abs(theta2-target_theta):
            return n_diff1
        return n_diff2

    ##binary search across integer n_diff to find right theta for length
    while abs(theta-target_theta) > tolerance:
    
        if theta < target_theta:
            n_diff_min = n_diff
        else:
            n_diff_max = n_diff
    
        n_diff = round((n_diff_max+n_diff_min)/2.0)
        if last_n_diff == n_diff:
            if n_diff-1 < n_diff_min:
                if n_diff+1 > n_diff_max:
                    break
                else:
                    n_diff += 1
            else:
                n_diff -= 1
    
        if n_diff == n_diff_max or n_diff == n_diff_min:# we've already tried this one
            n_diff = tie_break(n_diff, last_n_diff)
            break
    
        theta = calc_bend_angle(n, n_diff)
        last_n_diff = n_diff

    return n_diff
for num_potential_strands in range(num_potential_strands_range[0], num_potential_strands_range[1]+1):
    min_num_segments = int(math.ceil(num_segments_range[0]/float(num_potential_strands))*num_potential_strands)
    for num_segments in range(min_num_segments, num_segments_range[1]+1, num_potential_strands):

        if num_segments % 3 != 0:
            continue
        if num_segments_min > num_segments:
            continue


        n = num_segments*num_bp_per_segment
        n_diff = solve_for_n_diff(n)

        num_segments_factors = factors(num_segments)
        for i, val in enumerate(num_segments_factors):
            remainder = n_diff/val - round(n_diff/val)
            if abs(remainder*val) < tolerance and (round(n_diff/val) in num_segments_factors):
                factor1 = val
                factor2 = round(n_diff/val)
                n_diff = factor1*factor2
                print_candidate(num_segments, n_diff, num_potential_strands, num_segments/max(factor1, factor2))


print "\n\n"
print "candidate with min theta error:"
print_candidate(min_theta_error_candidate["num_segments"], min_theta_error_candidate["n_diff"],
                min_theta_error_candidate["num_potential_strands"], min_theta_error_candidate["n_diff_period"])
print calc_bend_angle(min_theta_error_candidate["num_segments"]*num_bp_per_segment, min_theta_error_candidate["n_diff"], True, False, True)
print "\n"