def for_exact(var, from, to, step)
	for(var, from+0.0, to+step/2.0, step+0.0)



num rb_smoothness = 1.1
rgb_colour rb_cols[24]

struct rgb_colour
	num red
	num green
	num blue
#
#hsv_init()
##	repeat
##		FILE *f = freopen("./colours", "r", stdin)
##		if f != NULL
##			break
#	freopen("./colours", "r", stdin)  # FIXME
#	
#	int i = 0
#	read_tsv(red_, green_, blue_)
#		if i == 24
#			error("too many lines in colours file!")
#		num red = atof(red_)
#		num green = atof(green_)
#		num blue = atof(blue_)
##			num white = atof(white_)
##			num black = atof(black_)
##			whiten() ; blacken()
##		red *= red_factor
##		green *= green_factor
##		blue *= blue_factor
#		
#		rb_cols[i].red = red
#		rb_cols[i].green = green
#		rb_cols[i].blue = blue
#		
#		++i
#	
#	if i != 24
#		error("not enough lines in colours file!")
#
#
#def rainbow(a) hsv(a, 1, 0)
#def hsv(a, s, v) hsv_(rad2lun(angle2rad(a)), s, v)
#colour hsv_(num a, num s, num v)
#	# from [0, 12)
#	a = rmod(a, 12)
#	a *= 2
#	int i0 = a
#	int i1 = (i0+1) % 24
#	num blend = (a - i0)
#	blend = pow(fabs(blend-0.5) * 2, 1/rb_smoothness)*fsgn(blend-0.5)/2 + 0.5
#	num red0 = rb_cols[i0].red
#	num green0 = rb_cols[i0].green
#	num blue0 = rb_cols[i0].blue
#	num red1 = rb_cols[i1].red
#	num green1 = rb_cols[i1].green
#	num blue1 = rb_cols[i1].blue
#	num red = (red1-red0)*blend+red0
#	num green = (green1-green0)*blend+green0
#	num blue = (blue1-blue0)*blend+blue0
#	num black = 1 - s
#	num white = v
#	blacken()
#	whiten()
#	return rgb(red, green, blue)

def whiten(x)
	x = 1 - (1-x)*(1-white)
def blacken(x)
	x = x*(1-black)
def whiten()
	whiten(red)
	whiten(green)
	whiten(blue)
def blacken()
	blacken(red)
	blacken(green)
	blacken(blue)

export vec util

def read_tsv_vec(v)
	read_tsv_vec(v, my(l))
def read_tsv_vec(v, l)
	repeat
		cstr l = Input()
		if l == NULL
			break
		splitv(v, l)

def read_tsv_vec_n(v, n)
	read_tsv_vec_n(v, n, my(l))
def read_tsv_vec_n(v, n, l)
	read_tsv_vec(v, l)
		if vec_get_size(v) != n
			error("read_tsv_vec_n: expected %d columns, got %d", n, vec_get_size(v))

# this is a degenerate case! but they are usually important to have
def read_tsv()
	_read_tsv(my(v))
def _read_tsv(v)
	new(v, vec, cstr, 0)
	read_tsv_vec_n(v, 0)
		.
#FIXME Free(v)!

# this is sort of degenerate but not really!
def read_tsv(a1)
	_read_tsv(my(v), a1)
def _read_tsv(v, a1)
	new(v, vec, cstr, 1)
	read_tsv_vec_n(v, 1)
		cstr a1 = *(cstr *)vec_element(v, 0)
#FIXME Free(v)!

def read_tsv(a1, a2)
	_read_tsv(my(v), a1, a2)
def _read_tsv(v, a1, a2)
	new(v, vec, cstr, 2)
	read_tsv_vec_n(v, 2)
		cstr a1 = *(cstr *)vec_element(v, 0)
		cstr a2 = *(cstr *)vec_element(v, 1)
#FIXME Free(v)!

def read_tsv(a1, a2, a3)
	_read_tsv(my(v), a1, a2, a3)
def _read_tsv(v, a1, a2, a3)
	new(v, vec, cstr, 3)
	read_tsv_vec_n(v, 3)
		cstr a1 = *(cstr *)vec_element(v, 0)
		cstr a2 = *(cstr *)vec_element(v, 1)
		cstr a3 = *(cstr *)vec_element(v, 2)
#FIXME Free(v)!

def read_tsv(a1, a2, a3, a4)
	_read_tsv(my(v), a1, a2, a3, a4)
def _read_tsv(v, a1, a2, a3, a4)
	new(v, vec, cstr, 4)
	read_tsv_vec_n(v, 4)
		cstr a1 = *(cstr *)vec_element(v, 0)
		cstr a2 = *(cstr *)vec_element(v, 1)
		cstr a3 = *(cstr *)vec_element(v, 2)
		cstr a4 = *(cstr *)vec_element(v, 3)
#FIXME Free(v)!

def read_tsv(a1, a2, a3, a4, a5)
	_read_tsv(my(v), a1, a2, a3, a4, a5)
def _read_tsv(v, a1, a2, a3, a4, a5)
	new(v, vec, cstr, 5)
	read_tsv_vec_n(v, 5)
		cstr a1 = *(cstr *)vec_element(v, 0)
		cstr a2 = *(cstr *)vec_element(v, 1)
		cstr a3 = *(cstr *)vec_element(v, 2)
		cstr a4 = *(cstr *)vec_element(v, 3)
		cstr a5 = *(cstr *)vec_element(v, 4)
#FIXME Free(v)!

# etc!  need snazzy variadic macros :)

# FIXME is this split, fixed s->i, in the latest brace?  I think not

splitv(vec *v, cstr s, char c)
	vec_clear(v)
	vec_push(v, s)
	for_cstr(i, s)
		if *i == c
			*i = '\0'
			vec_push(v, i+1)

def splitv(v, s) splitv(v, s, '\t')

def for_cstr(i, s)
	char *i
	for i=s; *i != '\0'; ++i
		.

# move to m.b
num fsgn(num x)
	if x == 0.0
		return 0.0
	if x < 0.0
		return -1.0
	return 1.0

# to m.b - new angle unit, lunes.. after the moon/months.. ok?
num lun2rad(num a)
	return a * pi / 6.0
num rad2lun(num a)
	return a * 6.0 / pi

# m.b

def rmod(r, low, high) rmod_range(r, low, high)
def rdiv(r, low, high) rdiv_range(r, low, high)
num rmod_range(num r, num low, num high)
	int d = rdiv_range(r, low, high)
	return r - d * (high-low)
num rdiv_range(num r, num low, num high)
	return rdiv(r-low, high-low)

# TODO rdiv/rmod variants based on ceiling instead of floor
#   (i.e. include top but not bottom of range?)


# TODO functions that compute sin and cos together?
def circular_blend(x, y, A, a)
	num y = Sin(a)/Sin(A)
	num x = Cos(a) - Cos(A)*y
	
	# this resolves an angle a into components parallel to two axes
	# which are at an angle A
	
	# to be used for fixing the colour space
	
	# could also be used for drawing circular arcs directly
	# in a projection of 3d, etc
	
	# what's a better name for it?  vector_arc?

# TODO functions that compute sin and cos together?

# I changed the semantics of circular_blend, now A comes last, and i is from 0
# to 1, not a from 0 to A.  this is so can put the limiting A=0 for a linear blend

### NO, CHANGED BACK: also I swapped x and y as the 0->1 one has more priority

# maybe should do a 3-arg form that only does one part (x/p1) of the blend

def circular_blend(x, y, i, A)
	if A == 0
		y = i
		x = 1-i
	 else
	 	num my(a) = A*i
		y = Sin(my(a))/Sin(A)
		x = Cos(my(a)) - Cos(A)*y

	# this resolves an angle a into components parallel to two axes
	# which are at an angle A
	
	# to be used for fixing the colour space
	
	# could also be used for drawing circular arcs directly
	# in a projection of 3d, etc
	
	# what's a better name for it?  vector_arc?

	# symmetrical, i.e. circular_blend(y, x, 1-i, A) is the same result
