Slightly increase amount of bandwidth of resampling filter for
feeder_rate_quality=3. This have the benefit of reducing aliasing
artifacts due to alias masking.

Spectrogram analysis:

 o Old preset (100:36:0.90)
	http://people.freebsd.org/~ariff/z_comparison/z_q3_old.png

 o New preset (100:36:0.92):
	http://people.freebsd.org/~ariff/z_comparison/z_q3_new.png

Approved by:	re (kib)

#!/usr/bin/awk -f
#
# Copyright (c) 2007-2009 Ariff Abdullah <ariff@FreeBSD.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$
#

#
# FIR filter design by windowing method. This might become one of the
# funniest joke I've ever written due to too many tricks being applied to
# ensure maximum precision (well, in fact this is already have the same
# precision granularity compared to its C counterpart). Nevertheless, it's
# working, precise, dynamically tunable based on "presets".
#
# XXX EXPECT TOTAL REWRITE! DON'T ARGUE!
#
# TODO: Using ultraspherical window might be a good idea.
#
# Based on:
#
# "Digital Audio Resampling" by Julius O. Smith III
#
#  - http://ccrma.stanford.edu/~jos/resample/
#

#
# Some basic Math functions.
#
function abs(x)
{
	return (((x < 0) ? -x : x) + 0);
}

function fabs(x)
{
	return (((x < 0.0) ? -x : x) + 0.0);
}

function ceil(x, r)
{
	r = int(x);
	if (r < x)
		r++;
	return (r + 0);
}

function floor(x, r)
{
	r = int(x);
	if (r > x)
		r--;
	return (r + 0);
}

function pow(x, y)
{
	return (exp(1.0 * y * log(1.0 * x)));
}

#
# What the hell...
#
function shl(x, y)
{
	while (y > 0) {
		x *= 2;
		y--;
	}
	return (x);
}

function shr(x, y)
{
	while (y > 0 && x != 0) {
		x = floor(x / 2);
		y--;
	}
	return (x);
}

function fx_floor(v, o, r)
{
	if (fabs(v) < fabs(smallest))
		smallest = v;
	if (fabs(v) > fabs(largest))
		largest = v;

	r = floor((v * o) + 0.5);
	if (r < INT32_MIN || r > INT32_MAX)
		printf("\n#error overflow v=%f, please reduce %d\n", v, o);

	return (r);
}

#
# Kaiser linear piecewise functions.
#
function kaiserAttn2Beta(attn, beta)
{
	if (attn < 0.0)
		return (Z_KAISER_BETA_DEFAULT);

	if (attn > 50.0)
		beta = 0.1102 * ((1.0 * attn) - 8.7);
	else if (attn > 21.0)
		beta = (0.5842 * pow((1.0 * attn) - 21.0, 0.4)) +	\
		    (0.07886 * ((1.0 * attn) - 21.0));
	else
		beta = 0.0;

	return (beta);
}

function kaiserBeta2Attn(beta, x, y, i, attn, xbeta)
{
	if (beta < Z_WINDOW_KAISER)
		return (Z_KAISER_ATTN_DEFAULT);

	if (beta > kaiserAttn2Beta(50.0))
		attn = ((1.0 * beta) / 0.1102) + 8.7;
	else {
		x = 21.0;
		y = 50.0;
		attn = 0.5 * (x + y);
		for (i = 0; i < 128; i++) {
			xbeta = kaiserAttn2Beta(attn)
			if (beta == xbeta ||				\
			    (i > 63 &&					\
			    fabs(beta - xbeta) < Z_KAISER_EPSILON))
				break;
			if (beta > xbeta)
				x = attn;
			else
				y = attn;
			attn = 0.5 * (x + y);
		}
	}

	return (attn);
}

function kaiserRolloff(len, attn)
{
	return (1.0 - (((1.0 * attn) - 7.95) / (((1.0 * len) - 1.0) * 14.36)));
}

#
# 0th order modified Bessel function of the first kind.
#
function I0(x, s, u, n, h, t)
{
	s = n = u = 1.0;
	h = x * 0.5;

	do {
		t = h / n;
		n += 1.0;
		t *= t;
		u *= t;
		s += u;
	} while (u >= (I0_EPSILON * s));

	return (s);
}

function wname(beta)
{
	if (beta >= Z_WINDOW_KAISER)
		return ("Kaiser");
	else if (beta == Z_WINDOW_BLACKMAN_NUTTALL)
		return ("Blackman - Nuttall");
	else if (beta == Z_WINDOW_NUTTALL)
		return ("Nuttall");
	else if (beta == Z_WINDOW_BLACKMAN_HARRIS)
		return ("Blackman - Harris");
	else if (beta == Z_WINDOW_BLACKMAN)
		return ("Blackman");
	else if (beta == Z_WINDOW_HAMMING)
		return ("Hamming");
	else if (beta == Z_WINDOW_HANN)
		return ("Hann");
	else
		return ("What The Hell !?!?");
}

function rolloff_round(x)
{
	if (x < 0.67)
		x = 0.67;
	else if (x > 1.0)
		x = 1.0;

	return (x);
}

function tap_round(x, y)
{
	y = floor(x + 3);
	y -= y % 4;
	return (y);
}

function lpf(imp, n, rolloff, beta, num, i, j, x, nm, ibeta, w)
{
	rolloff = rolloff_round(rolloff + (Z_NYQUIST_HOVER * (1.0 - rolloff)));
	imp[0] = rolloff;

	#
	# Generate ideal sinc impulses, locate the last zero-crossing and pad
	# the remaining with 0.
	#
	# Note that there are other (faster) ways of calculating this without
	# the misery of traversing the entire sinc given the fact that the
	# distance between each zero crossings is actually the bandwidth of
	# the impulses, but it seems having 0.0001% chances of failure due to
	# limited precision.
	#
	j = n;
	for (i = 1; i < n; i++) {
		x = (M_PI * i) / (1.0 * num);
		imp[i] = sin(x * rolloff) / x;
		if (i != 1 && (imp[i] * imp[i - 1]) <= 0.0)
			j = i;
	}

	for (i = j; i < n; i++)
		imp[i] = 0.0;

	nm = 1.0 * (j - 1);

	if (beta >= Z_WINDOW_KAISER)
		ibeta = I0(beta);

	for (i = 1; i < j; i++) {
		if (beta >= Z_WINDOW_KAISER) {
			# Kaiser window...
			x = (1.0 * i) / nm;
			w = I0(beta * sqrt(1.0 - (x * x))) / ibeta;
		} else {
			# Cosined windows...
			x = (M_PI * i) / nm;
			if (beta == Z_WINDOW_BLACKMAN_NUTTALL) {
				# Blackman - Nuttall
				w = 0.36335819 + (0.4891775 * cos(x)) +	\
				    (0.1365995 * cos(2 * x)) +		\
				    (0.0106411 * cos(3 * x));
			} else if (beta == Z_WINDOW_NUTTALL) {
				# Nuttall
		        	w = 0.355768 + (0.487396 * cos(x)) +	\
				    (0.144232 * cos(2 * x)) +		\
				    (0.012604 * cos(3 * x));
			} else if (beta == Z_WINDOW_BLACKMAN_HARRIS) {
				# Blackman - Harris
				w = 0.422323 + (0.49755 * cos(x)) +	\
				    (0.07922 * cos(2 * x));
			} else if (beta == Z_WINDOW_BLACKMAN) {
				# Blackman
				w = 0.42 + (0.50 * cos(x)) +		\
				    (0.08 * cos(2 * x));
			} else if (beta == Z_WINDOW_HAMMING) {
				# Hamming
				w = 0.54 + (0.46 * cos(x));
			} else if (beta == Z_WINDOW_HANN) {
				# Hann
				w = 0.50 + (0.50 * cos(x));
			} else {
				# What The Hell !?!?
				w = 0.0;
			}
		}
		imp[i] *= w;
	}

	imp["impulse_length"] = j;
	imp["rolloff"] = rolloff;
}

function mkfilter(imp, nmult, rolloff, beta, num,			\
    nwing, mwing, nrolloff, i, dcgain, v, quality)
{
	nwing = floor((nmult * num) / 2) + 1;

	lpf(imp, nwing, rolloff, beta, num);

	mwing = imp["impulse_length"];
	nrolloff = imp["rolloff"];
	quality = imp["quality"];

	dcgain = 0.0;
	for (i = num; i < mwing; i += num)
		dcgain += imp[i];
	dcgain *= 2.0;
	dcgain += imp[0];

	for (i = 0; i < nwing; i++)
		imp[i] /= dcgain;

	if (quality > 2)
		printf("\n");
	printf("/*\n");
	printf(" *   quality = %d\n", quality);
	printf(" *    window = %s\n", wname(beta));
	if (beta >= Z_WINDOW_KAISER) {
		printf(" *             beta: %.2f\n", beta);
		printf(" *             stop: -%.2f dB\n",		\
		    kaiserBeta2Attn(beta));
	}
	printf(" *    length = %d\n", nmult);
	printf(" * bandwidth = %.2f%%", rolloff * 100.0);
	if (rolloff != nrolloff) {
		printf(" + %.2f%% = %.2f%% (nyquist hover: %.2f%%)",	\
		    (nrolloff - rolloff) * 100.0, nrolloff * 100.0,	\
		    Z_NYQUIST_HOVER * 100.0);
	}
	printf("\n");
	printf(" *     drift = %d\n", num);
	printf(" *     width = %d\n", mwing);
	printf(" */\n");
	printf("static int32_t z_coeff_q%d[%d] = {",			\
	    quality, nwing + (Z_COEFF_OFFSET * 2));
	for (i = 0; i < (nwing + (Z_COEFF_OFFSET * 2)); i++) {
		if ((i % 5) == 0)
			printf("\n      ");
		if (i < Z_COEFF_OFFSET)
			v = fx_floor(imp[Z_COEFF_OFFSET - i], Z_COEFF_ONE);
		else if ((i - Z_COEFF_OFFSET) >= nwing)
			v = fx_floor(					\
			    imp[nwing + nwing - i + Z_COEFF_OFFSET - 1],\
			    Z_COEFF_ONE);
		else
			v = fx_floor(imp[i - Z_COEFF_OFFSET], Z_COEFF_ONE);
		printf(" %s0x%08x,", (v < 0) ? "-" : " ", abs(v));
	}
	printf("\n};\n\n");
	printf("/*\n");
	printf(" * interpolated q%d differences.\n", quality);
	printf(" */\n");
	printf("static int32_t z_dcoeff_q%d[%d] = {", quality, nwing);
	for (i = 1; i <= nwing; i++) {
		if ((i % 5) == 1)
			printf("\n      ");
		v = -imp[i - 1];
		if (i != nwing)
			v += imp[i];
		v = fx_floor(v, Z_INTERP_COEFF_ONE);
		if (abs(v) > abs(largest_interp))
			largest_interp = v;
		printf(" %s0x%08x,", (v < 0) ? "-" : " ", abs(v));
	}
	printf("\n};\n");

	return (nwing);
}

function filter_parse(s, a, i, attn, alen)
{
	split(s, a, ":");
	alen = length(a);

	if (alen > 0 && a[1] == "OVERSAMPLING_FACTOR") {
		if (alen != 2)
			return (-1);
		init_drift(floor(a[2]));
		return (-1);
	}

	if (alen == 1 || alen == 2) {
		if (a[1] == "NYQUIST_HOVER") {
			i = 1.0 * a[2];
			Z_NYQUIST_HOVER = (i > 0.0 && i < 1.0) ? i : 0.0;
			return (-1);
		}
		i = 1;
		if (alen == 1) {
			attn = Z_KAISER_ATTN_DEFAULT;
			Popts["beta"] = Z_KAISER_BETA_DEFAULT;
		} else if (Z_WINDOWS[a[1]] < Z_WINDOW_KAISER) {
			Popts["beta"] = Z_WINDOWS[a[1]];
			i = tap_round(a[2]);
			Popts["nmult"] = i;
			if (i < 28)
				i = 28;
			i = 1.0 - (6.44 / i);
			Popts["rolloff"] = rolloff_round(i);
			return (0);
		} else {
			attn = 1.0 * a[i++];
			Popts["beta"] = kaiserAttn2Beta(attn);
		}
		i = tap_round(a[i]);
		Popts["nmult"] = i;
		if (i > 7 && i < 28)
			i = 27;
		i = kaiserRolloff(i, attn);
		Popts["rolloff"] = rolloff_round(i);

		return (0);
	}

	if (!(alen == 3 || alen == 4))
		return (-1);

	i = 2;

	if (a[1] == "kaiser") {
		if (alen > 2)
			Popts["beta"] = 1.0 * a[i++];
		else
			Popts["beta"] = Z_KAISER_BETA_DEFAULT;
	} else if (Z_WINDOWS[a[1]] < Z_WINDOW_KAISER)
		Popts["beta"] = Z_WINDOWS[a[1]];
	else if (1.0 * a[1] < Z_WINDOW_KAISER)
		return (-1);
	else
		Popts["beta"] = kaiserAttn2Beta(1.0 * a[1]);
	Popts["nmult"] = tap_round(a[i++]);
	if (a[1] == "kaiser" && alen == 3)
		i = kaiserRolloff(Popts["nmult"],			\
		    kaiserBeta2Attn(Popts["beta"]));
	else
		i = 1.0 * a[i];
	Popts["rolloff"] = rolloff_round(i);

	return (0);
}

function genscale(bit, s1, s2, scale)
{
	s1 = Z_COEFF_SHIFT - (32 - bit);
	s2 = Z_SHIFT + (32 - bit);

	if (s1 == 0)
		scale = "v";
	else if (s1 < 0)
		scale = sprintf("(v) << %d", abs(s1));
	else
		scale = sprintf("(v) >> %d", s1);

	scale = sprintf("(%s) * Z_SCALE_CAST(s)", scale);

	if (s2 != 0)
		scale = sprintf("(%s) >> %d", scale, s2);

	printf("#define Z_SCALE_%d(v, s)\t%s(%s)\n",			\
	    bit, (bit < 10) ? "\t" : "", scale);
}

function genlerp(bit, use64, lerp)
{
	if ((bit + Z_LINEAR_SHIFT) <= 32) {
		lerp = sprintf("(((y) - (x)) * (z)) >> %d", Z_LINEAR_SHIFT);
	} else if (use64 != 0) {
		if ((bit + Z_LINEAR_SHIFT) <= 64) {
			lerp = sprintf(					\
			    "(((int64_t)(y) - (x)) * (z)) "		\
			    ">> %d",					\
			    Z_LINEAR_SHIFT);
		} else {
			lerp = sprintf(					\
			    "((int64_t)((y) >> %d) - ((x) >> %d)) * ",	\
			    "(z)"					\
			    bit + Z_LINEAR_SHIFT - 64,			\
			    bit + Z_LINEAR_SHIFT - 64);
			if ((64 - bit) != 0)
				lerp = sprintf("(%s) >> %d", lerp, 64 - bit);
		}
	} else {
		lerp = sprintf(						\
		    "(((y) >> %d) - ((x) >> %d)) * (z)",		\
		    bit + Z_LINEAR_SHIFT - 32,				\
		    bit + Z_LINEAR_SHIFT - 32);
		if ((32 - bit) != 0)
			lerp = sprintf("(%s) >> %d", lerp, 32 - bit);
	}

	printf("#define Z_LINEAR_INTERPOLATE_%d(z, x, y)"		\
	    "\t\t\t\t%s\\\n\t((x) + (%s))\n",					\
	    bit, (bit < 10) ? "\t" : "", lerp);
}

function init_drift(drift, xdrift)
{
	xdrift = floor(drift);

	if (Z_DRIFT_SHIFT != -1) {
		if (xdrift != Z_DRIFT_SHIFT)
			printf("#error Z_DRIFT_SHIFT reinitialize!\n");
		return;
	}

	#
	# Initialize filter oversampling factor, or in other word
	# Z_DRIFT_SHIFT.
	#
	if (xdrift < 0)
		xdrift = 1;
	else if (xdrift > 31)
		xdrift = 31;

	Z_DRIFT_SHIFT  = xdrift;
	Z_DRIFT_ONE    = shl(1, Z_DRIFT_SHIFT);

	Z_SHIFT        = Z_FULL_SHIFT - Z_DRIFT_SHIFT;
	Z_ONE          = shl(1, Z_SHIFT);
	Z_MASK         = Z_ONE - 1;
}

BEGIN {
	I0_EPSILON = 1e-21;
	M_PI = atan2(0.0, -1.0);

	INT32_MAX =  1 + ((shl(1, 30) - 1) * 2);
	INT32_MIN = -1 - INT32_MAX;

	Z_COEFF_OFFSET = 5;

	Z_FULL_SHIFT   = 30;
	Z_FULL_ONE     = shl(1, Z_FULL_SHIFT);

	Z_COEFF_SHIFT  = 28;
	Z_COEFF_ONE    = shl(1, Z_COEFF_SHIFT);

	Z_INTERP_COEFF_SHIFT = 24;
	Z_INTERP_COEFF_ONE   = shl(1, Z_INTERP_COEFF_SHIFT);

	Z_LINEAR_FULL_SHIFT = Z_FULL_SHIFT;
	Z_LINEAR_FULL_ONE   = shl(1, Z_LINEAR_FULL_SHIFT);
	Z_LINEAR_SHIFT      = 8;
	Z_LINEAR_UNSHIFT    = Z_LINEAR_FULL_SHIFT - Z_LINEAR_SHIFT;
	Z_LINEAR_ONE        = shl(1, Z_LINEAR_SHIFT)

	Z_DRIFT_SHIFT_DEFAULT = 5;
	Z_DRIFT_SHIFT         = -1;
	# meehhhh... let it overflow...
	#Z_SCALE_SHIFT   = 31;
	#Z_SCALE_ONE     = shl(1, Z_SCALE_SHIFT);

	Z_WINDOW_KAISER           =  0.0;
	Z_WINDOW_BLACKMAN_NUTTALL = -1.0;
	Z_WINDOW_NUTTALL          = -2.0;
	Z_WINDOW_BLACKMAN_HARRIS  = -3.0;
	Z_WINDOW_BLACKMAN         = -4.0;
	Z_WINDOW_HAMMING          = -5.0;
	Z_WINDOW_HANN             = -6.0;

	Z_WINDOWS["blackman_nuttall"] = Z_WINDOW_BLACKMAN_NUTTALL;
	Z_WINDOWS["nuttall"]          = Z_WINDOW_NUTTALL;
	Z_WINDOWS["blackman_harris"]  = Z_WINDOW_BLACKMAN_HARRIS;
	Z_WINDOWS["blackman"]         = Z_WINDOW_BLACKMAN;
	Z_WINDOWS["hamming"]          = Z_WINDOW_HAMMING;
	Z_WINDOWS["hann"]             = Z_WINDOW_HANN;

	Z_KAISER_2_BLACKMAN_BETA  = 8.568611;
	Z_KAISER_2_BLACKMAN_NUTTALL_BETA = 11.98;

	Z_KAISER_ATTN_DEFAULT = 100;
	Z_KAISER_BETA_DEFAULT = kaiserAttn2Beta(Z_KAISER_ATTN_DEFAULT);

	Z_KAISER_EPSILON = 1e-21;

	#
	# This is practically a joke.
	#
	Z_NYQUIST_HOVER = 0.0;

	smallest = 10.000000;
	largest  =  0.000010;
	largest_interp = 0;

	if (ARGC < 2) {
		ARGC = 1;
		ARGV[ARGC++] = "100:8:0.85";
		ARGV[ARGC++] = "100:36:0.92";
		ARGV[ARGC++] = "100:164:0.97";
		#ARGV[ARGC++] = "100:8";
		#ARGV[ARGC++] = "100:16";
		#ARGV[ARGC++] = "100:32:0.7929";
		#ARGV[ARGC++] = "100:64:0.8990";
		#ARGV[ARGC++] = "100:128:0.9499";
	}

	printf("#ifndef _FEEDER_RATE_GEN_H_\n");
	printf("#define _FEEDER_RATE_GEN_H_\n\n");
	printf("/*\n");
	printf(" * Generated using feeder_rate_mkfilter.awk, heaven, wind and awesome.\n");
	printf(" *\n");
	printf(" * DO NOT EDIT!\n");
	printf(" */\n\n");
	printf("#define FEEDER_RATE_PRESETS\t\"");
	for (i = 1; i < ARGC; i++)
		printf("%s%s", (i == 1) ? "" : " ", ARGV[i]);
	printf("\"\n\n");
	imp["quality"] = 2;
	for (i = 1; i < ARGC; i++) {
		if (filter_parse(ARGV[i]) == 0) {
			beta = Popts["beta"];
			nmult = Popts["nmult"];
			rolloff = Popts["rolloff"];
			if (Z_DRIFT_SHIFT == -1)
				init_drift(Z_DRIFT_SHIFT_DEFAULT);
			ztab[imp["quality"] - 2] =				\
			    mkfilter(imp, nmult, rolloff, beta, Z_DRIFT_ONE);
			imp["quality"]++;
		}
	}

	printf("\n");
	#
	# XXX
	#
	#if (length(ztab) > 0) {
	#	j = 0;
	#	for (i = 0; i < length(ztab); i++) {
	#		if (ztab[i] > j)
	#			j = ztab[i];
	#	}
	#	printf("static int32_t z_coeff_zero[%d] = {", j);
	#	for (i = 0; i < j; i++) {
	#		if ((i % 19) == 0)
	#			printf("\n");
	#		printf("  0,");
	#	}
	#	printf("\n};\n\n");
	#}
	#
	# XXX
	#
	printf("static const struct {\n");
	printf("\tint32_t len;\n");
	printf("\tint32_t *coeff;\n");
	printf("\tint32_t *dcoeff;\n");
	printf("} z_coeff_tab[] = {\n");
	if (length(ztab) > 0) {
		j = 0;
		for (i = 0; i < length(ztab); i++) {
			if (ztab[i] > j)
				j = ztab[i];
		}
		j = length(sprintf("%d", j));
		lfmt = sprintf("%%%dd", j);
		j = length(sprintf("z_coeff_q%d", length(ztab) + 1));
		zcfmt = sprintf("%%-%ds", j);
		zdcfmt = sprintf("%%-%ds", j + 1);

		for (i = 0; i < length(ztab); i++) {
			l = sprintf(lfmt, ztab[i]);
			zc = sprintf("z_coeff_q%d", i + 2);
			zc = sprintf(zcfmt, zc);
			zdc = sprintf("z_dcoeff_q%d", i + 2);
			zdc = sprintf(zdcfmt, zdc);
			printf("\t{ %s, %s, %s },\n", l, zc, zdc);
		}
	} else
		printf("\t{ 0, NULL, NULL }\n");
	printf("};\n\n");

	#Z_UNSHIFT = 0;
	#v = shr(Z_ONE - 1, Z_UNSHIFT) * abs(largest_interp);
	#while (v < 0 || v > INT32_MAX) {
	#	Z_UNSHIFT += 1;
	#	v = shr(Z_ONE - 1, Z_UNSHIFT) * abs(largest_interp);
	#}
	v = ((Z_ONE - 1) * abs(largest_interp)) / INT32_MAX;
	Z_UNSHIFT = ceil(log(v) / log(2.0));
	Z_INTERP_SHIFT = Z_SHIFT - Z_UNSHIFT + Z_INTERP_COEFF_SHIFT;

	Z_INTERP_UNSHIFT = (Z_SHIFT - Z_UNSHIFT) + Z_INTERP_COEFF_SHIFT	\
	    - Z_COEFF_SHIFT;

	printf("#define Z_COEFF_TAB_SIZE\t\t\t\t\t\t\\\n");
	printf("\t((int32_t)(sizeof(z_coeff_tab) /");
	printf(" sizeof(z_coeff_tab[0])))\n\n");
	printf("#define Z_COEFF_OFFSET\t\t%d\n\n", Z_COEFF_OFFSET);
	printf("#define Z_RSHIFT(x, y)\t\t(((x) + "			\
	    "(1 << ((y) - 1))) >> (y))\n");
	printf("#define Z_RSHIFT_L(x, y)\t(((x) + "			\
	    "(1LL << ((y) - 1))) >> (y))\n\n");
	printf("#define Z_FULL_SHIFT\t\t%d\n", Z_FULL_SHIFT);
	printf("#define Z_FULL_ONE\t\t0x%08x%s\n", Z_FULL_ONE,		\
	    (Z_FULL_ONE > INT32_MAX) ? "U" : "");
	printf("\n");
	printf("#define Z_DRIFT_SHIFT\t\t%d\n", Z_DRIFT_SHIFT);
	#printf("#define Z_DRIFT_ONE\t\t0x%08x\n", Z_DRIFT_ONE);
	printf("\n");
	printf("#define Z_SHIFT\t\t\t%d\n", Z_SHIFT);
	printf("#define Z_ONE\t\t\t0x%08x\n", Z_ONE);
	printf("#define Z_MASK\t\t\t0x%08x\n", Z_MASK);
	printf("\n");
	printf("#define Z_COEFF_SHIFT\t\t%d\n", Z_COEFF_SHIFT);
	zinterphp = "(z) * (d)";
	zinterpunshift = Z_SHIFT + Z_INTERP_COEFF_SHIFT - Z_COEFF_SHIFT;
	if (zinterpunshift > 0) {
		v = (Z_ONE - 1) * abs(largest_interp);
		if (v < INT32_MIN || v > INT32_MAX)
			zinterphp = sprintf("(int64_t)%s", zinterphp);
		zinterphp = sprintf("(%s) >> %d", zinterphp, zinterpunshift);
	} else if (zinterpunshift < 0)
		zinterphp = sprintf("(%s) << %d", zinterphp,		\
		    abs(zinterpunshift));
	if (Z_UNSHIFT == 0)
		zinterp = "z";
	else
		zinterp = sprintf("(z) >> %d", Z_UNSHIFT);
	zinterp = sprintf("(%s) * (d)", zinterp);
	if (Z_INTERP_UNSHIFT < 0)
		zinterp = sprintf("(%s) << %d", zinterp,		\
		    abs(Z_INTERP_UNSHIFT));
	else if (Z_INTERP_UNSHIFT > 0)
		zinterp = sprintf("(%s) >> %d", zinterp, Z_INTERP_UNSHIFT);
	if (zinterphp != zinterp) {
		printf("\n#ifdef SND_FEEDER_RATE_HP\n");
		printf("#define Z_COEFF_INTERPOLATE(z, c, d)"		\
		    "\t\t\t\t\t\\\n\t((c) + (%s))\n", zinterphp);
		printf("#else\n");
		printf("#define Z_COEFF_INTERPOLATE(z, c, d)"		\
		    "\t\t\t\t\t\\\n\t((c) + (%s))\n", zinterp);
		printf("#endif\n");
	} else
		printf("#define Z_COEFF_INTERPOLATE(z, c, d)"		\
		    "\t\t\t\t\t\\\n\t((c) + (%s))\n", zinterp);
	#printf("\n");
	#printf("#define Z_SCALE_SHIFT\t\t%d\n", Z_SCALE_SHIFT);
	#printf("#define Z_SCALE_ONE\t\t0x%08x%s\n", Z_SCALE_ONE,	\
	#    (Z_SCALE_ONE > INT32_MAX) ? "U" : "");
	printf("\n");
	printf("#define Z_SCALE_CAST(s)\t\t((uint32_t)(s))\n");
	genscale(8);
	genscale(16);
	genscale(24);
	genscale(32);
	printf("\n");
	printf("#define Z_LINEAR_FULL_ONE\t0x%08xU\n", Z_LINEAR_FULL_ONE);
	printf("#define Z_LINEAR_SHIFT\t\t%d\n", Z_LINEAR_SHIFT);
	printf("#define Z_LINEAR_UNSHIFT\t%d\n", Z_LINEAR_UNSHIFT);
	printf("#define Z_LINEAR_ONE\t\t0x%08x\n", Z_LINEAR_ONE);
	printf("\n");
	printf("#ifdef SND_PCM_64\n");
	genlerp(8, 1);
	genlerp(16, 1);
	genlerp(24, 1);
	genlerp(32, 1);
	printf("#else\t/* !SND_PCM_64 */\n");
	genlerp(8, 0);
	genlerp(16, 0);
	genlerp(24, 0);
	genlerp(32, 0);
	printf("#endif\t/* SND_PCM_64 */\n");
	printf("\n");
	printf("#define Z_QUALITY_ZOH\t\t0\n");
	printf("#define Z_QUALITY_LINEAR\t1\n");
	printf("#define Z_QUALITY_SINC\t\t%d\n",			\
	    floor((length(ztab) - 1) / 2) + 2);
	printf("\n");
	printf("#define Z_QUALITY_MIN\t\t0\n");
	printf("#define Z_QUALITY_MAX\t\t%d\n", length(ztab) + 1);
	printf("\n/*\n * smallest: %.32f\n *  largest: %.32f\n *\n",	\
	    smallest, largest);
	printf(" * z_unshift=%d, z_interp_shift=%d\n *\n",		\
	    Z_UNSHIFT, Z_INTERP_SHIFT);
	v = shr(Z_ONE - 1, Z_UNSHIFT) * abs(largest_interp);
	printf(" * largest interpolation multiplication: %d\n */\n", v);
	if (v < INT32_MIN || v > INT32_MAX) {
		printf("\n#ifndef SND_FEEDER_RATE_HP\n");
		printf("#error interpolation overflow, please reduce"	\
		    " Z_INTERP_SHIFT\n");
		printf("#endif\n");
	}

	printf("\n#endif /* !_FEEDER_RATE_GEN_H_ */\n");
}

#!/usr/bin/awk -f
#
# Copyright (c) 2007-2009 Ariff Abdullah <ariff@FreeBSD.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$
#

#
# FIR filter design by windowing method. This might become one of the
# funniest joke I've ever written due to too many tricks being applied to
# ensure maximum precision (well, in fact this is already have the same
# precision granularity compared to its C counterpart). Nevertheless, it's
# working, precise, dynamically tunable based on "presets".
#
# XXX EXPECT TOTAL REWRITE! DON'T ARGUE!
#
# TODO: Using ultraspherical window might be a good idea.
#
# Based on:
#
# "Digital Audio Resampling" by Julius O. Smith III
#
#  - http://ccrma.stanford.edu/~jos/resample/
#

#
# Some basic Math functions.
#
function abs(x)
{
	return (((x < 0) ? -x : x) + 0);
}

function fabs(x)
{
	return (((x < 0.0) ? -x : x) + 0.0);
}

function ceil(x, r)
{
	r = int(x);
	if (r < x)
		r++;
	return (r + 0);
}

function floor(x, r)
{
	r = int(x);
	if (r > x)
		r--;
	return (r + 0);
}

function pow(x, y)
{
	return (exp(1.0 * y * log(1.0 * x)));
}

#
# What the hell...
#
function shl(x, y)
{
	while (y > 0) {
		x *= 2;
		y--;
	}
	return (x);
}

function shr(x, y)
{
	while (y > 0 && x != 0) {
		x = floor(x / 2);
		y--;
	}
	return (x);
}

function fx_floor(v, o, r)
{
	if (fabs(v) < fabs(smallest))
		smallest = v;
	if (fabs(v) > fabs(largest))
		largest = v;

	r = floor((v * o) + 0.5);
	if (r < INT32_MIN || r > INT32_MAX)
		printf("\n#error overflow v=%f, please reduce %d\n", v, o);

	return (r);
}

#
# Kaiser linear piecewise functions.
#
function kaiserAttn2Beta(attn, beta)
{
	if (attn < 0.0)
		return (Z_KAISER_BETA_DEFAULT);

	if (attn > 50.0)
		beta = 0.1102 * ((1.0 * attn) - 8.7);
	else if (attn > 21.0)
		beta = (0.5842 * pow((1.0 * attn) - 21.0, 0.4)) +	\
		    (0.07886 * ((1.0 * attn) - 21.0));
	else
		beta = 0.0;

	return (beta);
}

function kaiserBeta2Attn(beta, x, y, i, attn, xbeta)
{
	if (beta < Z_WINDOW_KAISER)
		return (Z_KAISER_ATTN_DEFAULT);

	if (beta > kaiserAttn2Beta(50.0))
		attn = ((1.0 * beta) / 0.1102) + 8.7;
	else {
		x = 21.0;
		y = 50.0;
		attn = 0.5 * (x + y);
		for (i = 0; i < 128; i++) {
			xbeta = kaiserAttn2Beta(attn)
			if (beta == xbeta ||				\
			    (i > 63 &&					\
			    fabs(beta - xbeta) < Z_KAISER_EPSILON))
				break;
			if (beta > xbeta)
				x = attn;
			else
				y = attn;
			attn = 0.5 * (x + y);
		}
	}

	return (attn);
}

function kaiserRolloff(len, attn)
{
	return (1.0 - (((1.0 * attn) - 7.95) / (((1.0 * len) - 1.0) * 14.36)));
}

#
# 0th order modified Bessel function of the first kind.
#
function I0(x, s, u, n, h, t)
{
	s = n = u = 1.0;
	h = x * 0.5;

	do {
		t = h / n;
		n += 1.0;
		t *= t;
		u *= t;
		s += u;
	} while (u >= (I0_EPSILON * s));

	return (s);
}

function wname(beta)
{
	if (beta >= Z_WINDOW_KAISER)
		return ("Kaiser");
	else if (beta == Z_WINDOW_BLACKMAN_NUTTALL)
		return ("Blackman - Nuttall");
	else if (beta == Z_WINDOW_NUTTALL)
		return ("Nuttall");
	else if (beta == Z_WINDOW_BLACKMAN_HARRIS)
		return ("Blackman - Harris");
	else if (beta == Z_WINDOW_BLACKMAN)
		return ("Blackman");
	else if (beta == Z_WINDOW_HAMMING)
		return ("Hamming");
	else if (beta == Z_WINDOW_HANN)
		return ("Hann");
	else
		return ("What The Hell !?!?");
}

function rolloff_round(x)
{
	if (x < 0.67)
		x = 0.67;
	else if (x > 1.0)
		x = 1.0;

	return (x);
}

function tap_round(x, y)
{
	y = floor(x + 3);
	y -= y % 4;
	return (y);
}

function lpf(imp, n, rolloff, beta, num, i, j, x, nm, ibeta, w)
{
	rolloff = rolloff_round(rolloff + (Z_NYQUIST_HOVER * (1.0 - rolloff)));
	imp[0] = rolloff;

	#
	# Generate ideal sinc impulses, locate the last zero-crossing and pad
	# the remaining with 0.
	#
	# Note that there are other (faster) ways of calculating this without
	# the misery of traversing the entire sinc given the fact that the
	# distance between each zero crossings is actually the bandwidth of
	# the impulses, but it seems having 0.0001% chances of failure due to
	# limited precision.
	#
	j = n;
	for (i = 1; i < n; i++) {
		x = (M_PI * i) / (1.0 * num);
		imp[i] = sin(x * rolloff) / x;
		if (i != 1 && (imp[i] * imp[i - 1]) <= 0.0)
			j = i;
	}

	for (i = j; i < n; i++)
		imp[i] = 0.0;

	nm = 1.0 * (j - 1);

	if (beta >= Z_WINDOW_KAISER)
		ibeta = I0(beta);

	for (i = 1; i < j; i++) {
		if (beta >= Z_WINDOW_KAISER) {
			# Kaiser window...
			x = (1.0 * i) / nm;
			w = I0(beta * sqrt(1.0 - (x * x))) / ibeta;
		} else {
			# Cosined windows...
			x = (M_PI * i) / nm;
			if (beta == Z_WINDOW_BLACKMAN_NUTTALL) {
				# Blackman - Nuttall
				w = 0.36335819 + (0.4891775 * cos(x)) +	\
				    (0.1365995 * cos(2 * x)) +		\
				    (0.0106411 * cos(3 * x));
			} else if (beta == Z_WINDOW_NUTTALL) {
				# Nuttall
		        	w = 0.355768 + (0.487396 * cos(x)) +	\
				    (0.144232 * cos(2 * x)) +		\
				    (0.012604 * cos(3 * x));
			} else if (beta == Z_WINDOW_BLACKMAN_HARRIS) {
				# Blackman - Harris
				w = 0.422323 + (0.49755 * cos(x)) +	\
				    (0.07922 * cos(2 * x));
			} else if (beta == Z_WINDOW_BLACKMAN) {
				# Blackman
				w = 0.42 + (0.50 * cos(x)) +		\
				    (0.08 * cos(2 * x));
			} else if (beta == Z_WINDOW_HAMMING) {
				# Hamming
				w = 0.54 + (0.46 * cos(x));
			} else if (beta == Z_WINDOW_HANN) {
				# Hann
				w = 0.50 + (0.50 * cos(x));
			} else {
				# What The Hell !?!?
				w = 0.0;
			}
		}
		imp[i] *= w;
	}

	imp["impulse_length"] = j;
	imp["rolloff"] = rolloff;
}

function mkfilter(imp, nmult, rolloff, beta, num,			\
    nwing, mwing, nrolloff, i, dcgain, v, quality)
{
	nwing = floor((nmult * num) / 2) + 1;

	lpf(imp, nwing, rolloff, beta, num);

	mwing = imp["impulse_length"];
	nrolloff = imp["rolloff"];
	quality = imp["quality"];

	dcgain = 0.0;
	for (i = num; i < mwing; i += num)
		dcgain += imp[i];
	dcgain *= 2.0;
	dcgain += imp[0];

	for (i = 0; i < nwing; i++)
		imp[i] /= dcgain;

	if (quality > 2)
		printf("\n");
	printf("/*\n");
	printf(" *   quality = %d\n", quality);
	printf(" *    window = %s\n", wname(beta));
	if (beta >= Z_WINDOW_KAISER) {
		printf(" *             beta: %.2f\n", beta);
		printf(" *             stop: -%.2f dB\n",		\
		    kaiserBeta2Attn(beta));
	}
	printf(" *    length = %d\n", nmult);
	printf(" * bandwidth = %.2f%%", rolloff * 100.0);
	if (rolloff != nrolloff) {
		printf(" + %.2f%% = %.2f%% (nyquist hover: %.2f%%)",	\
		    (nrolloff - rolloff) * 100.0, nrolloff * 100.0,	\
		    Z_NYQUIST_HOVER * 100.0);
	}
	printf("\n");
	printf(" *     drift = %d\n", num);
	printf(" *     width = %d\n", mwing);
	printf(" */\n");
	printf("static int32_t z_coeff_q%d[%d] = {",			\
	    quality, nwing + (Z_COEFF_OFFSET * 2));
	for (i = 0; i < (nwing + (Z_COEFF_OFFSET * 2)); i++) {
		if ((i % 5) == 0)
			printf("\n      ");
		if (i < Z_COEFF_OFFSET)
			v = fx_floor(imp[Z_COEFF_OFFSET - i], Z_COEFF_ONE);
		else if ((i - Z_COEFF_OFFSET) >= nwing)
			v = fx_floor(					\
			    imp[nwing + nwing - i + Z_COEFF_OFFSET - 1],\
			    Z_COEFF_ONE);
		else
			v = fx_floor(imp[i - Z_COEFF_OFFSET], Z_COEFF_ONE);
		printf(" %s0x%08x,", (v < 0) ? "-" : " ", abs(v));
	}
	printf("\n};\n\n");
	printf("/*\n");
	printf(" * interpolated q%d differences.\n", quality);
	printf(" */\n");
	printf("static int32_t z_dcoeff_q%d[%d] = {", quality, nwing);
	for (i = 1; i <= nwing; i++) {
		if ((i % 5) == 1)
			printf("\n      ");
		v = -imp[i - 1];
		if (i != nwing)
			v += imp[i];
		v = fx_floor(v, Z_INTERP_COEFF_ONE);
		if (abs(v) > abs(largest_interp))
			largest_interp = v;
		printf(" %s0x%08x,", (v < 0) ? "-" : " ", abs(v));
	}
	printf("\n};\n");

	return (nwing);
}

function filter_parse(s, a, i, attn, alen)
{
	split(s, a, ":");
	alen = length(a);

	if (alen > 0 && a[1] == "OVERSAMPLING_FACTOR") {
		if (alen != 2)
			return (-1);
		init_drift(floor(a[2]));
		return (-1);
	}

	if (alen == 1 || alen == 2) {
		if (a[1] == "NYQUIST_HOVER") {
			i = 1.0 * a[2];
			Z_NYQUIST_HOVER = (i > 0.0 && i < 1.0) ? i : 0.0;
			return (-1);
		}
		i = 1;
		if (alen == 1) {
			attn = Z_KAISER_ATTN_DEFAULT;
			Popts["beta"] = Z_KAISER_BETA_DEFAULT;
		} else if (Z_WINDOWS[a[1]] < Z_WINDOW_KAISER) {
			Popts["beta"] = Z_WINDOWS[a[1]];
			i = tap_round(a[2]);
			Popts["nmult"] = i;
			if (i < 28)
				i = 28;
			i = 1.0 - (6.44 / i);
			Popts["rolloff"] = rolloff_round(i);
			return (0);
		} else {
			attn = 1.0 * a[i++];
			Popts["beta"] = kaiserAttn2Beta(attn);
		}
		i = tap_round(a[i]);
		Popts["nmult"] = i;
		if (i > 7 && i < 28)
			i = 27;
		i = kaiserRolloff(i, attn);
		Popts["rolloff"] = rolloff_round(i);

		return (0);
	}

	if (!(alen == 3 || alen == 4))
		return (-1);

	i = 2;

	if (a[1] == "kaiser") {
		if (alen > 2)
			Popts["beta"] = 1.0 * a[i++];
		else
			Popts["beta"] = Z_KAISER_BETA_DEFAULT;
	} else if (Z_WINDOWS[a[1]] < Z_WINDOW_KAISER)
		Popts["beta"] = Z_WINDOWS[a[1]];
	else if (1.0 * a[1] < Z_WINDOW_KAISER)
		return (-1);
	else
		Popts["beta"] = kaiserAttn2Beta(1.0 * a[1]);
	Popts["nmult"] = tap_round(a[i++]);
	if (a[1] == "kaiser" && alen == 3)
		i = kaiserRolloff(Popts["nmult"],			\
		    kaiserBeta2Attn(Popts["beta"]));
	else
		i = 1.0 * a[i];
	Popts["rolloff"] = rolloff_round(i);

	return (0);
}

function genscale(bit, s1, s2, scale)
{
	s1 = Z_COEFF_SHIFT - (32 - bit);
	s2 = Z_SHIFT + (32 - bit);

	if (s1 == 0)
		scale = "v";
	else if (s1 < 0)
		scale = sprintf("(v) << %d", abs(s1));
	else
		scale = sprintf("(v) >> %d", s1);

	scale = sprintf("(%s) * Z_SCALE_CAST(s)", scale);

	if (s2 != 0)
		scale = sprintf("(%s) >> %d", scale, s2);

	printf("#define Z_SCALE_%d(v, s)\t%s(%s)\n",			\
	    bit, (bit < 10) ? "\t" : "", scale);
}

function genlerp(bit, use64, lerp)
{
	if ((bit + Z_LINEAR_SHIFT) <= 32) {
		lerp = sprintf("(((y) - (x)) * (z)) >> %d", Z_LINEAR_SHIFT);
	} else if (use64 != 0) {
		if ((bit + Z_LINEAR_SHIFT) <= 64) {
			lerp = sprintf(					\
			    "(((int64_t)(y) - (x)) * (z)) "		\
			    ">> %d",					\
			    Z_LINEAR_SHIFT);
		} else {
			lerp = sprintf(					\
			    "((int64_t)((y) >> %d) - ((x) >> %d)) * ",	\
			    "(z)"					\
			    bit + Z_LINEAR_SHIFT - 64,			\
			    bit + Z_LINEAR_SHIFT - 64);
			if ((64 - bit) != 0)
				lerp = sprintf("(%s) >> %d", lerp, 64 - bit);
		}
	} else {
		lerp = sprintf(						\
		    "(((y) >> %d) - ((x) >> %d)) * (z)",		\
		    bit + Z_LINEAR_SHIFT - 32,				\
		    bit + Z_LINEAR_SHIFT - 32);
		if ((32 - bit) != 0)
			lerp = sprintf("(%s) >> %d", lerp, 32 - bit);
	}

	printf("#define Z_LINEAR_INTERPOLATE_%d(z, x, y)"		\
	    "\t\t\t\t%s\\\n\t((x) + (%s))\n",					\
	    bit, (bit < 10) ? "\t" : "", lerp);
}

function init_drift(drift, xdrift)
{
	xdrift = floor(drift);

	if (Z_DRIFT_SHIFT != -1) {
		if (xdrift != Z_DRIFT_SHIFT)
			printf("#error Z_DRIFT_SHIFT reinitialize!\n");
		return;
	}

	#
	# Initialize filter oversampling factor, or in other word
	# Z_DRIFT_SHIFT.
	#
	if (xdrift < 0)
		xdrift = 1;
	else if (xdrift > 31)
		xdrift = 31;

	Z_DRIFT_SHIFT  = xdrift;
	Z_DRIFT_ONE    = shl(1, Z_DRIFT_SHIFT);

	Z_SHIFT        = Z_FULL_SHIFT - Z_DRIFT_SHIFT;
	Z_ONE          = shl(1, Z_SHIFT);
	Z_MASK         = Z_ONE - 1;
}

BEGIN {
	I0_EPSILON = 1e-21;
	M_PI = atan2(0.0, -1.0);

	INT32_MAX =  1 + ((shl(1, 30) - 1) * 2);
	INT32_MIN = -1 - INT32_MAX;

	Z_COEFF_OFFSET = 5;

	Z_FULL_SHIFT   = 30;
	Z_FULL_ONE     = shl(1, Z_FULL_SHIFT);

	Z_COEFF_SHIFT  = 28;
	Z_COEFF_ONE    = shl(1, Z_COEFF_SHIFT);

	Z_INTERP_COEFF_SHIFT = 24;
	Z_INTERP_COEFF_ONE   = shl(1, Z_INTERP_COEFF_SHIFT);

	Z_LINEAR_FULL_SHIFT = Z_FULL_SHIFT;
	Z_LINEAR_FULL_ONE   = shl(1, Z_LINEAR_FULL_SHIFT);
	Z_LINEAR_SHIFT      = 8;
	Z_LINEAR_UNSHIFT    = Z_LINEAR_FULL_SHIFT - Z_LINEAR_SHIFT;
	Z_LINEAR_ONE        = shl(1, Z_LINEAR_SHIFT)

	Z_DRIFT_SHIFT_DEFAULT = 5;
	Z_DRIFT_SHIFT         = -1;
	# meehhhh... let it overflow...
	#Z_SCALE_SHIFT   = 31;
	#Z_SCALE_ONE     = shl(1, Z_SCALE_SHIFT);

	Z_WINDOW_KAISER           =  0.0;
	Z_WINDOW_BLACKMAN_NUTTALL = -1.0;
	Z_WINDOW_NUTTALL          = -2.0;
	Z_WINDOW_BLACKMAN_HARRIS  = -3.0;
	Z_WINDOW_BLACKMAN         = -4.0;
	Z_WINDOW_HAMMING          = -5.0;
	Z_WINDOW_HANN             = -6.0;

	Z_WINDOWS["blackman_nuttall"] = Z_WINDOW_BLACKMAN_NUTTALL;
	Z_WINDOWS["nuttall"]          = Z_WINDOW_NUTTALL;
	Z_WINDOWS["blackman_harris"]  = Z_WINDOW_BLACKMAN_HARRIS;
	Z_WINDOWS["blackman"]         = Z_WINDOW_BLACKMAN;
	Z_WINDOWS["hamming"]          = Z_WINDOW_HAMMING;
	Z_WINDOWS["hann"]             = Z_WINDOW_HANN;

	Z_KAISER_2_BLACKMAN_BETA  = 8.568611;
	Z_KAISER_2_BLACKMAN_NUTTALL_BETA = 11.98;

	Z_KAISER_ATTN_DEFAULT = 100;
	Z_KAISER_BETA_DEFAULT = kaiserAttn2Beta(Z_KAISER_ATTN_DEFAULT);

	Z_KAISER_EPSILON = 1e-21;

	#
	# This is practically a joke.
	#
	Z_NYQUIST_HOVER = 0.0;

	smallest = 10.000000;
	largest  =  0.000010;
	largest_interp = 0;

	if (ARGC < 2) {
		ARGC = 1;
		ARGV[ARGC++] = "100:8:0.85";
		ARGV[ARGC++] = "100:36:0.90";
		ARGV[ARGC++] = "100:164:0.97";
		#ARGV[ARGC++] = "100:8";
		#ARGV[ARGC++] = "100:16";
		#ARGV[ARGC++] = "100:32:0.7929";
		#ARGV[ARGC++] = "100:64:0.8990";
		#ARGV[ARGC++] = "100:128:0.9499";
	}

	printf("#ifndef _FEEDER_RATE_GEN_H_\n");
	printf("#define _FEEDER_RATE_GEN_H_\n\n");
	printf("/*\n");
	printf(" * Generated using feeder_rate_mkfilter.awk, heaven, wind and awesome.\n");
	printf(" *\n");
	printf(" * DO NOT EDIT!\n");
	printf(" */\n\n");
	printf("#define FEEDER_RATE_PRESETS\t\"");
	for (i = 1; i < ARGC; i++)
		printf("%s%s", (i == 1) ? "" : " ", ARGV[i]);
	printf("\"\n\n");
	imp["quality"] = 2;
	for (i = 1; i < ARGC; i++) {
		if (filter_parse(ARGV[i]) == 0) {
			beta = Popts["beta"];
			nmult = Popts["nmult"];
			rolloff = Popts["rolloff"];
			if (Z_DRIFT_SHIFT == -1)
				init_drift(Z_DRIFT_SHIFT_DEFAULT);
			ztab[imp["quality"] - 2] =				\
			    mkfilter(imp, nmult, rolloff, beta, Z_DRIFT_ONE);
			imp["quality"]++;
		}
	}

	printf("\n");
	#
	# XXX
	#
	#if (length(ztab) > 0) {
	#	j = 0;
	#	for (i = 0; i < length(ztab); i++) {
	#		if (ztab[i] > j)
	#			j = ztab[i];
	#	}
	#	printf("static int32_t z_coeff_zero[%d] = {", j);
	#	for (i = 0; i < j; i++) {
	#		if ((i % 19) == 0)
	#			printf("\n");
	#		printf("  0,");
	#	}
	#	printf("\n};\n\n");
	#}
	#
	# XXX
	#
	printf("static const struct {\n");
	printf("\tint32_t len;\n");
	printf("\tint32_t *coeff;\n");
	printf("\tint32_t *dcoeff;\n");
	printf("} z_coeff_tab[] = {\n");
	if (length(ztab) > 0) {
		j = 0;
		for (i = 0; i < length(ztab); i++) {
			if (ztab[i] > j)
				j = ztab[i];
		}
		j = length(sprintf("%d", j));
		lfmt = sprintf("%%%dd", j);
		j = length(sprintf("z_coeff_q%d", length(ztab) + 1));
		zcfmt = sprintf("%%-%ds", j);
		zdcfmt = sprintf("%%-%ds", j + 1);

		for (i = 0; i < length(ztab); i++) {
			l = sprintf(lfmt, ztab[i]);
			zc = sprintf("z_coeff_q%d", i + 2);
			zc = sprintf(zcfmt, zc);
			zdc = sprintf("z_dcoeff_q%d", i + 2);
			zdc = sprintf(zdcfmt, zdc);
			printf("\t{ %s, %s, %s },\n", l, zc, zdc);
		}
	} else
		printf("\t{ 0, NULL, NULL }\n");
	printf("};\n\n");

	#Z_UNSHIFT = 0;
	#v = shr(Z_ONE - 1, Z_UNSHIFT) * abs(largest_interp);
	#while (v < 0 || v > INT32_MAX) {
	#	Z_UNSHIFT += 1;
	#	v = shr(Z_ONE - 1, Z_UNSHIFT) * abs(largest_interp);
	#}
	v = ((Z_ONE - 1) * abs(largest_interp)) / INT32_MAX;
	Z_UNSHIFT = ceil(log(v) / log(2.0));
	Z_INTERP_SHIFT = Z_SHIFT - Z_UNSHIFT + Z_INTERP_COEFF_SHIFT;

	Z_INTERP_UNSHIFT = (Z_SHIFT - Z_UNSHIFT) + Z_INTERP_COEFF_SHIFT	\
	    - Z_COEFF_SHIFT;

	printf("#define Z_COEFF_TAB_SIZE\t\t\t\t\t\t\\\n");
	printf("\t((int32_t)(sizeof(z_coeff_tab) /");
	printf(" sizeof(z_coeff_tab[0])))\n\n");
	printf("#define Z_COEFF_OFFSET\t\t%d\n\n", Z_COEFF_OFFSET);
	printf("#define Z_RSHIFT(x, y)\t\t(((x) + "			\
	    "(1 << ((y) - 1))) >> (y))\n");
	printf("#define Z_RSHIFT_L(x, y)\t(((x) + "			\
	    "(1LL << ((y) - 1))) >> (y))\n\n");
	printf("#define Z_FULL_SHIFT\t\t%d\n", Z_FULL_SHIFT);
	printf("#define Z_FULL_ONE\t\t0x%08x%s\n", Z_FULL_ONE,		\
	    (Z_FULL_ONE > INT32_MAX) ? "U" : "");
	printf("\n");
	printf("#define Z_DRIFT_SHIFT\t\t%d\n", Z_DRIFT_SHIFT);
	#printf("#define Z_DRIFT_ONE\t\t0x%08x\n", Z_DRIFT_ONE);
	printf("\n");
	printf("#define Z_SHIFT\t\t\t%d\n", Z_SHIFT);
	printf("#define Z_ONE\t\t\t0x%08x\n", Z_ONE);
	printf("#define Z_MASK\t\t\t0x%08x\n", Z_MASK);
	printf("\n");
	printf("#define Z_COEFF_SHIFT\t\t%d\n", Z_COEFF_SHIFT);
	zinterphp = "(z) * (d)";
	zinterpunshift = Z_SHIFT + Z_INTERP_COEFF_SHIFT - Z_COEFF_SHIFT;
	if (zinterpunshift > 0) {
		v = (Z_ONE - 1) * abs(largest_interp);
		if (v < INT32_MIN || v > INT32_MAX)
			zinterphp = sprintf("(int64_t)%s", zinterphp);
		zinterphp = sprintf("(%s) >> %d", zinterphp, zinterpunshift);
	} else if (zinterpunshift < 0)
		zinterphp = sprintf("(%s) << %d", zinterphp,		\
		    abs(zinterpunshift));
	if (Z_UNSHIFT == 0)
		zinterp = "z";
	else
		zinterp = sprintf("(z) >> %d", Z_UNSHIFT);
	zinterp = sprintf("(%s) * (d)", zinterp);
	if (Z_INTERP_UNSHIFT < 0)
		zinterp = sprintf("(%s) << %d", zinterp,		\
		    abs(Z_INTERP_UNSHIFT));
	else if (Z_INTERP_UNSHIFT > 0)
		zinterp = sprintf("(%s) >> %d", zinterp, Z_INTERP_UNSHIFT);
	if (zinterphp != zinterp) {
		printf("\n#ifdef SND_FEEDER_RATE_HP\n");
		printf("#define Z_COEFF_INTERPOLATE(z, c, d)"		\
		    "\t\t\t\t\t\\\n\t((c) + (%s))\n", zinterphp);
		printf("#else\n");
		printf("#define Z_COEFF_INTERPOLATE(z, c, d)"		\
		    "\t\t\t\t\t\\\n\t((c) + (%s))\n", zinterp);
		printf("#endif\n");
	} else
		printf("#define Z_COEFF_INTERPOLATE(z, c, d)"		\
		    "\t\t\t\t\t\\\n\t((c) + (%s))\n", zinterp);
	#printf("\n");
	#printf("#define Z_SCALE_SHIFT\t\t%d\n", Z_SCALE_SHIFT);
	#printf("#define Z_SCALE_ONE\t\t0x%08x%s\n", Z_SCALE_ONE,	\
	#    (Z_SCALE_ONE > INT32_MAX) ? "U" : "");
	printf("\n");
	printf("#define Z_SCALE_CAST(s)\t\t((uint32_t)(s))\n");
	genscale(8);
	genscale(16);
	genscale(24);
	genscale(32);
	printf("\n");
	printf("#define Z_LINEAR_FULL_ONE\t0x%08xU\n", Z_LINEAR_FULL_ONE);
	printf("#define Z_LINEAR_SHIFT\t\t%d\n", Z_LINEAR_SHIFT);
	printf("#define Z_LINEAR_UNSHIFT\t%d\n", Z_LINEAR_UNSHIFT);
	printf("#define Z_LINEAR_ONE\t\t0x%08x\n", Z_LINEAR_ONE);
	printf("\n");
	printf("#ifdef SND_PCM_64\n");
	genlerp(8, 1);
	genlerp(16, 1);
	genlerp(24, 1);
	genlerp(32, 1);
	printf("#else\t/* !SND_PCM_64 */\n");
	genlerp(8, 0);
	genlerp(16, 0);
	genlerp(24, 0);
	genlerp(32, 0);
	printf("#endif\t/* SND_PCM_64 */\n");
	printf("\n");
	printf("#define Z_QUALITY_ZOH\t\t0\n");
	printf("#define Z_QUALITY_LINEAR\t1\n");
	printf("#define Z_QUALITY_SINC\t\t%d\n",			\
	    floor((length(ztab) - 1) / 2) + 2);
	printf("\n");
	printf("#define Z_QUALITY_MIN\t\t0\n");
	printf("#define Z_QUALITY_MAX\t\t%d\n", length(ztab) + 1);
	printf("\n/*\n * smallest: %.32f\n *  largest: %.32f\n *\n",	\
	    smallest, largest);
	printf(" * z_unshift=%d, z_interp_shift=%d\n *\n",		\
	    Z_UNSHIFT, Z_INTERP_SHIFT);
	v = shr(Z_ONE - 1, Z_UNSHIFT) * abs(largest_interp);
	printf(" * largest interpolation multiplication: %d\n */\n", v);
	if (v < INT32_MIN || v > INT32_MAX) {
		printf("\n#ifndef SND_FEEDER_RATE_HP\n");
		printf("#error interpolation overflow, please reduce"	\
		    " Z_INTERP_SHIFT\n");
		printf("#endif\n");
	}

	printf("\n#endif /* !_FEEDER_RATE_GEN_H_ */\n");
}


layers.awk: handle comments properly in layers.txt

Signed-off-by: Denys Dmytriyenko <d29de71aea38aad3a87d486929cb0aad173ae612@ti.com>
Signed-off-by: Koen Kooi <42e3d96910f922bdd7dd1805fe3f8aff634f391d@dominion.thruhere.net>

BEGIN {
	FS =",";
	print "Configured layers:\n";
 }

$1 !~ "^#" { print "layer repository name: " $1 "\nlayer uri: " $2 "\nlayer branch/revision: " $3 "/" $4 ; system("${OE_BASE}/scripts/layerman " $1 " "  $2 " " $3 " " $4 " " command " " commandarg);}

BEGIN {
	FS =",";
	print "Configured layers:\n";
 }

$1 != "# Name" { print "layer repository name: " $1 "\nlayer uri: " $2 "\nlayer branch/revision: " $3 "/" $4 ; system("${OE_BASE}/scripts/layerman " $1 " "  $2 " " $3 " " $4 " " command " " commandarg);}


INTEGRATION: CWS changefileheader (1.2.478); FILE MERGED 2008/03/28 15:55:32 rt 1.2.478.1: #i87441# Change license header to LPGL v3.

#*************************************************************************
#
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# Copyright 2008 by Sun Microsystems, Inc.
#
# OpenOffice.org - a multi-platform office productivity suite
#
# $RCSfile: unxmap-to-macosx-explist.awk,v $
#
# $Revision: 1.3 $
#
# This file is part of OpenOffice.org.
#
# OpenOffice.org is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# only, as published by the Free Software Foundation.
#
# OpenOffice.org is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License version 3 for more details
# (a copy is included in the LICENSE file that accompanied this code).
#
# You should have received a copy of the GNU Lesser General Public License
# version 3 along with OpenOffice.org.  If not, see
# <http://www.openoffice.org/license.html>
# for a copy of the LGPLv3 License.
#
#*************************************************************************

# Generate an exported symbols list out of a map file (as use on Linux/Solaris) in order to
# build shared libraries on Mac OS X
#
# The below code fails may fail with 'perverted' mapfiles (using a strange line layout etc.)

# Skip 'SECTION_NAME {' lines
/^[\t ]*.*[\t ]*\{/ { next }

# Skip 'global:' or 'local:' lines
/global:/ || /local:/ { next }

# Skip '*;' lines
/^[\t ]*\*;[\t ]*/ { next }

# Skip section end '}?;' lines
/^[\t ]*\}[\t ]*.*[;]*/ { next }

# Skip comment or empty lines
/^[\t ]*#.*/ || /^[\t ]*$/ || /^$/ { next }

# Echo all lines containing symbol names and prefix them with '_'
# because symbols on Mac OS X start always with '__'
{
    # There may appear multiple symbols in one line
    # e.g. "sym1; sym2; # and finally a comment"
    # take this into account
    for (i = 1; i <= NF ; i++) {
	if ($i !~ /^[\t ]*#.*/) { # as long as the current field doesn't start with '#'
	    gsub(/[\t ;]/, "", $i) # Remove leading spaces and trailing ';'
	    printf("_%s\n",$i)
	}
	else { # ignore everything after a '#' (comment) sign
	    break
	}
    }
}

#*************************************************************************
#
#   OpenOffice.org - a multi-platform office productivity suite
#
#   $RCSfile: unxmap-to-macosx-explist.awk,v $
#
#   $Revision: 1.2 $
#
#   last change: $Author: rt $ $Date: 2007-01-31 08:42:05 $
#
#   The Contents of this file are made available subject to
#   the terms of GNU Lesser General Public License Version 2.1.
#
#
#     GNU Lesser General Public License Version 2.1
#     =============================================
#     Copyright 2005 by Sun Microsystems, Inc.
#     901 San Antonio Road, Palo Alto, CA 94303, USA
#
#     This library is free software; you can redistribute it and/or
#     modify it under the terms of the GNU Lesser General Public
#     License version 2.1, as published by the Free Software Foundation.
#
#     This library is distributed in the hope that it will be useful,
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#     Lesser General Public License for more details.
#
#     You should have received a copy of the GNU Lesser General Public
#     License along with this library; if not, write to the Free Software
#     Foundation, Inc., 59 Temple Place, Suite 330, Boston,
#     MA  02111-1307  USA
#
#*************************************************************************

# Generate an exported symbols list out of a map file (as use on Linux/Solaris) in order to
# build shared libraries on Mac OS X
#
# The below code fails may fail with 'perverted' mapfiles (using a strange line layout etc.)

# Skip 'SECTION_NAME {' lines
/^[\t ]*.*[\t ]*\{/ { next }

# Skip 'global:' or 'local:' lines
/global:/ || /local:/ { next }

# Skip '*;' lines
/^[\t ]*\*;[\t ]*/ { next }

# Skip section end '}?;' lines
/^[\t ]*\}[\t ]*.*[;]*/ { next }

# Skip comment or empty lines
/^[\t ]*#.*/ || /^[\t ]*$/ || /^$/ { next }

# Echo all lines containing symbol names and prefix them with '_'
# because symbols on Mac OS X start always with '__'
{
    # There may appear multiple symbols in one line
    # e.g. "sym1; sym2; # and finally a comment"
    # take this into account
    for (i = 1; i <= NF ; i++) {
	if ($i !~ /^[\t ]*#.*/) { # as long as the current field doesn't start with '#'
	    gsub(/[\t ;]/, "", $i) # Remove leading spaces and trailing ';'
	    printf("_%s\n",$i)
	}
	else { # ignore everything after a '#' (comment) sign
	    break
	}
    }
}


Minor change so that my testbox at home is marked as a pc300.


More tinkering with the street finder

More tinkering with the street finder

function AU_init(addr_info, us_states, us_states_long, towns_a2q, towns_r2q, st_types_2qry, dirs_2qry, ords_2qry,    err, ad_counts, ad) {

	err = 0

	# check that we got all the data we need
	ad_counts["n_us_states"] = AU_get_addr_data(addr_info, "us_states", us_states)

	ad_counts["n_towns_a2q"] = AU_get_addr_data(addr_info, "towns_a2q", towns_a2q)
	ad_counts["n_st_types_2qry"] = AU_get_addr_data(addr_info, "st_types_2qry", st_types_2qry)
	ad_counts["n_dirs_2qry"] = AU_get_addr_data(addr_info, "dirs_2qry", dirs_2qry)
	ad_counts["n_ords_2qry"] = AU_get_addr_data(addr_info, "ords_2qry", ords_2qry)

	ad_counts["n_towns_r2q"] = AU_get_addr_data(addr_info, "towns_r2q", towns_r2q)

	for(ad in ad_counts){
		if(ad_counts[ad] == 0){
			printf("ERROR: %s no \"%s\" data\n", ai_file, substr(ad, 3)) > "/dev/stderr"
			err = 1
		}
	}
	if(err)
		return err

	# create a map of full state names
	for(s in us_states)
		us_states_long[us_states[s]] = s

	return err
}

function AU_parse(options, addr, addr_ary, states, states_long, towns, st_types, dirs, st_ords,   nf, ary, i, f_st, f_twn, nf2, ary2, b1, e1, name, street, quals, town, state, work) {

	addr_ary["status"] = "B"
	addr_ary["emsg"  ] = ""
	addr_ary["name"  ] = ""
	addr_ary["street"] = ""
	addr_ary["quals" ] = ""
	addr_ary["town"  ] = ""
	addr_ary["state" ] = ""

	nf = split(addr, ary, ",")
	for(i = 1; i <= nf; i++){
		sub(/^ */, "", ary[i])
		sub(/ *$/, "", ary[i])
	}

	# Some geocoders are US only so the last field is the state or state zip
	if(options["rply"]){
		if(!options["us_only"]){
			if(ary[nf] == "United States of America")
				nf--
			else{
				addr_ary["emsg"] = "not.US"
				return 1
			}
		}
	}

	if(!options["rply"]){
		# As a convenience to the user, given that nearly all the addresses
		# are in small set of nearby towns, the minimal query address can be
		#
		#	street, TOWN
		#
		# where TOWN is an abbreviation for town, state. Example: PA -> Palo Alto, CA
		if(nf < 2){
			addr_ary["emsg"] = "short.addr.1"
			return 1
		}
		# expand town if needed
		if(ary[nf] in towns){
			nf2 = split(towns[ary[nf]], ary2, ",")
			if(nf2 != 2){
				addr_ary["emsg"] = "bad.town"
				return  1
			}
			for(i = 1; i <= nf2; i++){
				sub(/^  */, "", ary2[i])
				sub(/  *$/, "", ary2[i])
			}
			ary[nf] = ary2[1]
			ary[nf+1] = ary2[2]
			nf++
		}
	}

	# At this point, all addresses must have at least three fields: street, town, state
	if(nf < 3){
		addr_ary["emsg"] = "short.addr.2"
		return 1
	}

	# check for a valid state, ignoring zip code
	if(substr(ary[nf], 1, 2) in states)
		state = ary[nf]
	else if(ary[nf] in states_long)
		state = ary[nf]
	else{
		addr_ary["emsg"] = sprintf("bad.US.state.%s", ary[nf])
		return 1
	}

	town = ary[nf-1]

	f_st = 0

	# find the street:
	# street is the last element in ary[1:nf-1] that begins with an integer and has more
	# than one word so, 200 by itself is not a street, but 200 Broadway is
	f_st = 0
	for(i = 1; i < nf - 1; i++){
		nf2 = split(ary[i], ary2, /  */)
		if(ary2[1] ~ /^[1-9][0-9]*$/ && nf2 > 1){
			f_st = i
		}
	}
	if(f_st == 0){
		addr_ary["emsg"] = "no.street.num"
		return 1
	}

	# The name is what precedes f_st.  if f_st == 1, set name to user supplied default value
	if(f_st == 1)
		name = options["no_name"]
	else{
		name = ary[1]
		for(i = 2; i < f_st; i++)
			name = name ", " ary[i]
	}

	# Street qualifiers are those fields, if any, that follow f_st and precede nf - 1
	if(nf - f_st > 2){
		quals = ary[f_st+1]
		for(i = f_st+2; i < nf - 1; i++)
			quals = quals ", " ary[i]
	}else
		quals = ""

	# clean up street
	nf2 = split(ary[f_st], ary2, /  */)
	if(nf2 < 2){
		addr_ary["emsg"] = "short.street"
		return 1
	}

	# If a word in street begins w/lower case, upper case it.  Convert, say el Camino to El Camino, etc
	for(i = 2; i <= nf2; i++){
		if(substr(ary2[i], 1, 1) ~ /^[a-z]/)
			ary2[i] = toupper(substr(ary2[i], 1, 1)) substr(ary2[i], 2)
	}

	# Street should be num [ dir ] str [ st ] [ dir ]
	street = ary2[1]
	if(options["do_subs"]){
		# TODO: generalize, as I've now seen an address w/3 words in dir, st_types
		# This is amusing.  What is South Court? Is it S. Court or South Ct.  No idea so leave such streets in long form
		if(nf2 == 3 && (ary2[2] in dirs) && (ary2[3] in st_types)){
			for(i = 2; i <= nf2; i++)
				street = street " " ary2[i]
		}else{
			# handle any leading direction
			if(ary2[2] in dirs){
				street = street " " dirs[ary2[2]]
				b1 = 3
			}else
				b1 = 2

			# detect trailing direction
			e1 = ary2[nf2] in dirs ? nf2-1 : nf2
			for(i = b1; i < e1; i++){
				if(i == b1)
					street = street ((ary2[i] in st_ords) ? (" " st_ords[ary2[i]]) : (" " ary2[i]))
				else
					street = street " " ary2[i]
			}
			street = street ((ary2[e1] in st_types) ? (" " st_types[ary2[e1]]) : (" " ary2[e1]))
			# handle any trailing direction
			if(e1 < nf2)
				street = street " " dirs[ary2[nf2]]
		}
	}else{
		for(i = 2; i <= nf2; i++)
			street = street " " ary2[i]
	}

	if(options["rply"]){
		if(town in towns)
			town = towns[town]
	}

	addr_ary["status"] = "G"
	addr_ary["name"  ] = name
	addr_ary["street"] = street
	addr_ary["quals" ] = quals
	addr_ary["town"  ] = town
	addr_ary["state" ] = state

	return 0
}

function AU_get_addr_data(addr_info, key, data,   k, keys, nk, i) {

	n_data = 0
	for(k in addr_info){
		nk = split(k, keys, SUBSEP)
		if(keys[1] == key){
			data[keys[2]] = addr_info[k]
			n_data++
		}
	}
	return n_data
}

function AU_match(options, ref, cand,   nc_fields, c_fields, nr_fields, r_fields, i, nc_rtab, c_rtab, nr_rtab, r_rtab, nr_nwords, r_nwords, nc_nwords, c_nwords) {

	if(options["verbose"]){
		printf("ref.name    = %s\n", ref["name"]) > "/dev/stderr"
		printf("ref.street  = %s\n", ref["street"]) > "/dev/stderr"
		printf("ref.town    = %s\n", ref["town"]) > "/dev/stderr"
		printf("ref.state   = %s\n", ref["state"]) > "/dev/stderr"
		printf("cand.name   = %s\n", cand["name"]) > "/dev/stderr"
		printf("cand.street = %s\n", cand["street"]) > "/dev/stderr"
		printf("cand.town   = %s\n", cand["town"]) > "/dev/stderr"
		printf("cand.state  = %s\n", cand["state"]) > "/dev/stderr"
	}

	cand["emsg"] = ""
	if(cand["state"] != ref["state"]){
		if(options["ign_zip"]){
			if(substr(cand["state"], 1, 2) != substr(ref["state"], 1, 2)){
				cand["emsg"] = "states.ign_zip.diff"
				return 0
			}
		}else{
			cand["emsg"] = "states.diff"
			return 0
		}
	}

	if(cand["town"] != ref["town"]){
		cand["emsg"] = "towns.diff"
		return 0
	}

	# street numbers and/or ranges, etc is 1st field
	nc_fields = split(cand["street"], c_fields, /  */)
	nr_fields = split(ref["street"], r_fields, /  */)

	# check that non-number parts of streets agree
	if(nc_fields != nr_fields){
		cand["emsg"] = "num.st.fields.diff"
		return 0
	}
	for(i = 2; i <= nc_fields; i++){
		if(c_fields[i] != r_fields[i]){
			cand["emsg"] = sprintf("st.field.%d.diff|%s|%s", i, c_fields[i], r_fields[i])
			return 0
		}
	}

	nc_rtab = AU_get_rtab(c_fields[1], c_rtab)
	if(nc_rtab == 0){
		cand["emsg"] = "nc_rtab.is.zero"
		return 0
	}

	nr_rtab = AU_get_rtab(r_fields[1], r_rtab)
	if(nr_rtab == 0){
		cand["emsg"] = "nr_rtab.is.zero"
		return 0
	}

	if(!AU_rtabs_intersect(nc_rtab, c_rtab, nr_rtab, r_rtab)){
		cand["emsg"] = "rtab.no.ovlp"
		return 0
	}

	# deal with names
	if(cand["name"] == ref["name"]){
		return 3
	}else if(cand["name"] == ""){	# this is an unannotated address
		return 1
	}else if(ref["name"] == options["no_name"]){	# many destinations are just street, town, state
		return 1
	}else{	# check if 1st words match; but no matter what this is a match
		nc_nwords = split(cand["name"], c_nwords, /  */)
		nr_nwords = split(ref["name"], r_nwords, /  */)
		return c_nwords[1] == r_nwords[1] ? 2 : 1
	}
}

function AU_get_rtab(street, rtab,   n_rtab, i, nw, work, l_pfx, e_work2) {

	n_rtab = split(street, rtab, ";")
	for(i = 1; i <= n_rtab; i++){
		nw = split(rtab[i], work, "-")
		if(nw > 1){
			rtab[i, 1] = work[1] + 0	# force number
			rtab[i, 2] = work[2] + 0	# force number

			if(rtab[i,1] > rtab[i,2]){
				# Is this a "delta" range where the 2d value shows only the different address suffix?
				# If so try to fix it
				# For example 945-51 would be expanded to 945-951 which is good, but
				# 945-37 would be expanded to 945-937 which is still wrong.
				l_pfx = length(work[1]) - length(work[2])
				e_work2 = ((l_pfx > 0) ? ((substr(work[1], 1, l_pfx) work[2])) : work[2]) + 0
				# did we fix it?
				if(rtab[i, 1] > e_work2){
					printf("ERROR: bad range 1st precedes 2nd: %d-%d\n", rtab[i, 1], rtab[i, 2]) > "/dev/stderr"
					return 0
				}else
					rtab[i, 2] = e_work2
			}

			rtab[i, "rng"] = 1
			if(rtab[i, 1] % 2 != rtab[i, 2] % 2){
				printf("ERROR: bad range: odd/even %d-%d\n", rtab[i, 1], rtab[i, 2]) > "/dev/stderr"
				return 0
			}
		}else{
			rtab[i] = work[1] + 0	# force number
			rtab[i, "rng"] = 0
		}
	}
	return n_rtab
}

function AU_rtabs_intersect(n_rtab1, rtab1, n_rtab2, rtab2,   i, j) {

	# the most common case: number v number
	if(n_rtab1 == 1 && !rtab1[1, "rng"] && n_rtab2 == 1 && !rtab2[1, "rng"])
		return rtab1[1] == rtab2[1]

	for(i = 1; i <= n_rtab1; i++){
		for(j = 1; j <= n_rtab2; j++){
			if(!rtab1[i, "rng"]){
				if(!rtab2[j, "rng"]){	# number v number
					if(rtab1[i] == rtab2[j])
						return 1
				}else{			# number v range
					if(rtab1[i] >= rtab2[j, 1] && rtab1[i] <= rtab2[j, 2])
						return 1
				}
			}else if(!rtab2[j, "rng"]){	# range v number
				if(rtab2[j] >= rtab1[i, 1] && rtab2[j] <= rtab1[i, 2])
					return 1
			}else{				# range v range
				if(!(rtab1[i, 2] < rtab2[j, 1] || rtab1[i, 1] > rtab2[j, 2]))
					return 1
			}
		}
	}
	return 0
}

function AU_rtab_dump(file, n_rtab, rtab,   i) {

	printf("n_rtab = %d {\n", n_rtab) > file
	for(i = 1; i <= n_rtab; i++){
		if(rtab[i, "rng"])
			printf("\trtab[%d, 1-2] = %d-%d\n", i, rtab[i, 1], rtab[i, 2]) > file
		else
			printf("\trtab[%d     ] = %d\n", i, rtab[i]) > file
	}
	printf("}\n") > file
}

function AU_init(addr_info, us_states, us_states_long, towns_a2q, towns_r2q, st_types_2qry, dirs_2qry, ords_2qry,    err, ad_counts, ad) {

	err = 0

	# check that we got all the data we need
	ad_counts["n_us_states"] = AU_get_addr_data(addr_info, "us_states", us_states)

	ad_counts["n_towns_a2q"] = AU_get_addr_data(addr_info, "towns_a2q", towns_a2q)
	ad_counts["n_st_types_2qry"] = AU_get_addr_data(addr_info, "st_types_2qry", st_types_2qry)
	ad_counts["n_dirs_2qry"] = AU_get_addr_data(addr_info, "dirs_2qry", dirs_2qry)
	ad_counts["n_ords_2qry"] = AU_get_addr_data(addr_info, "ords_2qry", ords_2qry)

	ad_counts["n_towns_r2q"] = AU_get_addr_data(addr_info, "towns_r2q", towns_r2q)

	for(ad in ad_counts){
		if(ad_counts[ad] == 0){
			printf("ERROR: %s no \"%s\" data\n", ai_file, substr(ad, 3)) > "/dev/stderr"
			err = 1
		}
	}
	if(err)
		return err

	# create a map of full state names
	for(s in us_states)
		us_states_long[us_states[s]] = s

	return err
}

function AU_parse(options, addr, addr_ary, states, states_long, towns, st_types, dirs, st_ords,   nf, ary, i, f_st, f_twn, nf2, ary2, b1, e1, name, street, quals, town, state, work) {

	addr_ary["status"] = "B"
	addr_ary["emsg"  ] = ""
	addr_ary["name"  ] = ""
	addr_ary["street"] = ""
	addr_ary["quals" ] = ""
	addr_ary["town"  ] = ""
	addr_ary["state" ] = ""

	nf = split(addr, ary, ",")
	for(i = 1; i <= nf; i++){
		sub(/^ */, "", ary[i])
		sub(/ *$/, "", ary[i])
	}

	# Some geocoders are US only so the last field is the state or state zip
	if(options["rply"]){
		if(!options["us_only"]){
			if(ary[nf] == "United States of America")
				nf--
			else{
				addr_ary["emsg"] = "not.US"
				return 1
			}
		}
	}

	if(!options["rply"]){
		# As a convenience to the user, given that nearly all the addresses
		# are in small set of nearby towns, the minimal query address can be
		#
		#	street, TOWN
		#
		# where TOWN is an abbreviation for town, state. Example: PA -> Palo Alto, CA
		if(nf < 2){
			addr_ary["emsg"] = "short.addr.1"
			return 1
		}
		# expand town if needed
		if(ary[nf] in towns){
			nf2 = split(towns[ary[nf]], ary2, ",")
			if(nf2 != 2){
				addr_ary["emsg"] = "bad.town"
				return  1
			}
			for(i = 1; i <= nf2; i++){
				sub(/^  */, "", ary2[i])
				sub(/  *$/, "", ary2[i])
			}
			ary[nf] = ary2[1]
			ary[nf+1] = ary2[2]
			nf++
		}
	}

	# At this point, all addresses must have at least three fields: street, town, state
	if(nf < 3){
		addr_ary["emsg"] = "short.addr.2"
		return 1
	}

	# check for a valid state, ignoring zip code
	if(substr(ary[nf], 1, 2) in states)
		state = ary[nf]
	else if(ary[nf] in states_long)
		state = ary[nf]
	else{
		addr_ary["emsg"] = sprintf("bad.US.state.%s", ary[nf])
		return 1
	}

	town = ary[nf-1]

	f_st = 0

	# find the street:
	# street is the 1st element of ary that begins with an integer and has > 1 one word
	# so, 200 by itself is not a street, but 200 Broadway is
	f_st = 0
	for(i = 1; i < nf - 1; i++){
		nf2 = split(ary[i], ary2, /  */)
		if(ary2[1] ~ /^[1-9][0-9]*$/ && nf2 > 1){
			f_st = i
			break
		}
	}
	if(f_st == 0){
		addr_ary["emsg"] = "no.street.num"
		return 1
	}

	# The name is what precedes f_st.  if f_st == 1, set name to user supplied default value
	if(f_st == 1)
		name = options["no_name"]
	else{
		name = ary[1]
		for(i = 2; i < f_st; i++)
			name = name ", " ary[i]
	}

	# Street qualifiers are those fields, if any, that follow f_st and precede nf - 1
	if(nf - f_st > 2){
		quals = ary[f_st+1]
		for(i = f_st+2; i < nf - 1; i++)
			quals = quals ", " ary[i]
	}else
		quals = ""

	# clean up street
	nf2 = split(ary[f_st], ary2, /  */)
	if(nf2 < 2){
		addr_ary["emsg"] = "short.street"
		return 1
	}

	# If a word in street begins w/lower case, upper case it.  Convert, say el Camino to El Camino, etc
	for(i = 2; i <= nf2; i++){
		if(substr(ary2[i], 1, 1) ~ /^[a-z]/)
			ary2[i] = toupper(substr(ary2[i], 1, 1)) substr(ary2[i], 2)
	}

	# Street should be num [ dir ] str [ st ] [ dir ]
	street = ary2[1]
	if(options["do_subs"]){
		# TODO: generalize, as I've now seen an address w/3 words in dir, st_types
		# This is amusing.  What is South Court? Is it S. Court or South Ct.  No idea so leave such streets in long form
		if(nf2 == 3 && (ary2[2] in dirs) && (ary2[3] in st_types)){
			for(i = 2; i <= nf2; i++)
				street = street " " ary2[i]
		}else{
			# handle any leading direction
			if(ary2[2] in dirs){
				street = street " " dirs[ary2[2]]
				b1 = 3
			}else
				b1 = 2

			# detect trailing direction
			e1 = ary2[nf2] in dirs ? nf2-1 : nf2
			for(i = b1; i < e1; i++){
				if(i == b1)
					street = street ((ary2[i] in st_ords) ? (" " st_ords[ary2[i]]) : (" " ary2[i]))
				else
					street = street " " ary2[i]
			}
			street = street ((ary2[e1] in st_types) ? (" " st_types[ary2[e1]]) : (" " ary2[e1]))
			# handle any trailing direction
			if(e1 < nf2)
				street = street " " dirs[ary2[nf2]]
		}
	}else{
		for(i = 2; i <= nf2; i++)
			street = street " " ary2[i]
	}

	if(options["rply"]){
		if(town in towns)
			town = towns[town]
	}

	addr_ary["status"] = "G"
	addr_ary["name"  ] = name
	addr_ary["street"] = street
	addr_ary["quals" ] = quals
	addr_ary["town"  ] = town
	addr_ary["state" ] = state

	return 0
}

function AU_get_addr_data(addr_info, key, data,   k, keys, nk, i) {

	n_data = 0
	for(k in addr_info){
		nk = split(k, keys, SUBSEP)
		if(keys[1] == key){
			data[keys[2]] = addr_info[k]
			n_data++
		}
	}
	return n_data
}

function AU_match(options, ref, cand,   nc_fields, c_fields, nr_fields, r_fields, i, nc_rtab, c_rtab, nr_rtab, r_rtab, nr_nwords, r_nwords, nc_nwords, c_nwords) {

	if(options["verbose"]){
		printf("ref.name    = %s\n", ref["name"]) > "/dev/stderr"
		printf("ref.street  = %s\n", ref["street"]) > "/dev/stderr"
		printf("ref.town    = %s\n", ref["town"]) > "/dev/stderr"
		printf("ref.state   = %s\n", ref["state"]) > "/dev/stderr"
		printf("cand.name   = %s\n", cand["name"]) > "/dev/stderr"
		printf("cand.street = %s\n", cand["street"]) > "/dev/stderr"
		printf("cand.town   = %s\n", cand["town"]) > "/dev/stderr"
		printf("cand.state  = %s\n", cand["state"]) > "/dev/stderr"
	}

	cand["emsg"] = ""
	if(cand["state"] != ref["state"]){
		if(options["ign_zip"]){
			if(substr(cand["state"], 1, 2) != substr(ref["state"], 1, 2)){
				cand["emsg"] = "states.ign_zip.diff"
				return 0
			}
		}else{
			cand["emsg"] = "states.diff"
			return 0
		}
	}

	if(cand["town"] != ref["town"]){
		cand["emsg"] = "towns.diff"
		return 0
	}

	# street numbers and/or ranges, etc is 1st field
	nc_fields = split(cand["street"], c_fields, /  */)
	nr_fields = split(ref["street"], r_fields, /  */)

	# check that non-number parts of streets agree
	if(nc_fields != nr_fields){
		cand["emsg"] = "num.st.fields.diff"
		return 0
	}
	for(i = 2; i <= nc_fields; i++){
		if(c_fields[i] != r_fields[i]){
			cand["emsg"] = sprintf("st.field.%d.diff|%s|%s", i, c_fields[i], r_fields[i])
			return 0
		}
	}

	nc_rtab = AU_get_rtab(c_fields[1], c_rtab)
	if(nc_rtab == 0){
		cand["emsg"] = "nc_rtab.is.zero"
		return 0
	}

	nr_rtab = AU_get_rtab(r_fields[1], r_rtab)
	if(nr_rtab == 0){
		cand["emsg"] = "nr_rtab.is.zero"
		return 0
	}

	if(!AU_rtabs_intersect(nc_rtab, c_rtab, nr_rtab, r_rtab)){
		cand["emsg"] = "rtab.no.ovlp"
		return 0
	}

	# deal with names
	if(cand["name"] == ref["name"]){
		return 3
	}else if(cand["name"] == ""){	# this is an unannotated address
		return 1
	}else if(ref["name"] == options["no_name"]){	# many destinations are just street, town, state
		return 1
	}else{	# check if 1st words match; but no matter what this is a match
		nc_nwords = split(cand["name"], c_nwords, /  */)
		nr_nwords = split(ref["name"], r_nwords, /  */)
		return c_nwords[1] == r_nwords[1] ? 2 : 1
	}
}

function AU_get_rtab(street, rtab,   n_rtab, i, nw, work, l_pfx, e_work2) {

	n_rtab = split(street, rtab, ";")
	for(i = 1; i <= n_rtab; i++){
		nw = split(rtab[i], work, "-")
		if(nw > 1){
			rtab[i, 1] = work[1] + 0	# force number
			rtab[i, 2] = work[2] + 0	# force number

			if(rtab[i,1] > rtab[i,2]){
				# Is this a "delta" range where the 2d value shows only the different address suffix?
				# If so try to fix it
				# For example 945-51 would be expanded to 945-951 which is good, but
				# 945-37 would be expanded to 945-937 which is still wrong.
				l_pfx = length(work[1]) - length(work[2])
				e_work2 = ((l_pfx > 0) ? ((substr(work[1], 1, l_pfx) work[2])) : work[2]) + 0
				# did we fix it?
				if(rtab[i, 1] > e_work2){
					printf("ERROR: bad range 1st precedes 2nd: %d-%d\n", rtab[i, 1], rtab[i, 2]) > "/dev/stderr"
					return 0
				}else
					rtab[i, 2] = e_work2
			}

			rtab[i, "rng"] = 1
			if(rtab[i, 1] % 2 != rtab[i, 2] % 2){
				printf("ERROR: bad range: odd/even %d-%d\n", rtab[i, 1], rtab[i, 2]) > "/dev/stderr"
				return 0
			}
		}else{
			rtab[i] = work[1] + 0	# force number
			rtab[i, "rng"] = 0
		}
	}
	return n_rtab
}

function AU_rtabs_intersect(n_rtab1, rtab1, n_rtab2, rtab2,   i, j) {

	# the most common case: number v number
	if(n_rtab1 == 1 && !rtab1[1, "rng"] && n_rtab2 == 1 && !rtab2[1, "rng"])
		return rtab1[1] == rtab2[1]

	for(i = 1; i <= n_rtab1; i++){
		for(j = 1; j <= n_rtab2; j++){
			if(!rtab1[i, "rng"]){
				if(!rtab2[j, "rng"]){	# number v number
					if(rtab1[i] == rtab2[j])
						return 1
				}else{			# number v range
					if(rtab1[i] >= rtab2[j, 1] && rtab1[i] <= rtab2[j, 2])
						return 1
				}
			}else if(!rtab2[j, "rng"]){	# range v number
				if(rtab2[j] >= rtab1[i, 1] && rtab2[j] <= rtab1[i, 2])
					return 1
			}else{				# range v range
				if(!(rtab1[i, 2] < rtab2[j, 1] || rtab1[i, 1] > rtab2[j, 2]))
					return 1
			}
		}
	}
	return 0
}

function AU_rtab_dump(file, n_rtab, rtab,   i) {

	printf("n_rtab = %d {\n", n_rtab) > file
	for(i = 1; i <= n_rtab; i++){
		if(rtab[i, "rng"])
			printf("\trtab[%d, 1-2] = %d-%d\n", i, rtab[i, 1], rtab[i, 2]) > file
		else
			printf("\trtab[%d     ] = %d\n", i, rtab[i]) > file
	}
	printf("}\n") > file
}


Fix parsing on TCP header with options

Fix parsing on TCP header with options

#!/usr/bin/awk
func flushKey()
{
  if(buffer != 0)
  {
    key = substr(buffer, 0, keyLen);
    buffer = 0;
    keyPos = -1;
    keyLenPos = -1;
    extraLenPos = -1;
  }
}

BEGIN{
  buffer = 0;
  key = 0;
}
{
  if($1 ~ /[0-9]+:[0-9]+:[0-9]+\.[0-9]+/)
    flushKey();
  else if($1 ~ /0x[0-9]+:/)
  {
    pos = (substr($1,0,6) + 0);

    # capturing the begin of the tcp payload
    if(pos == 32)
    {
      # 4 * tcp header size + IP header size
      tcpBegin = ("0x"substr($2,0,1) + 0) * 4 + 20;
      keyLenPos = tcpBegin + 2;
      keyLenOffset = keyLenPos % 16;
      extraLenPos = tcpBegin + 4;
      extraLenOffset = extraLenPos % 16;
    }

    if(pos >= 32)
    {
      # get the key length
      if(pos == keyLenPos - keyLenOffset)
      {
        keyLen = ("0x" $(2 + keyLenOffset / 2)) + 0;
      }
      # get the extra length
      if(pos == extraLenPos - extraLenOffset)
      {
        extraLen = ("0x"substr($(2 + extraLenOffset / 2),0,2)) + 0;
        keyPos = tcpBegin + 24 + extraLen;
        keyOffset = keyPos % 16;
      }
      if(pos == keyPos - keyOffset)
      {
        buffer = substr($NF, keyOffset+1)
      }
      else if(buffer != 0)
      {
        buffer = buffer $NF;
        if(length(buffer) >= keyLen)
          flushKey();
      }
    }
  }

  if(key != 0)
  {
    print key;
    key = 0;
  }
}
END{
  flushKey();
}

#!/usr/bin/awk
func flushKey()
{
  if(buffer != 0)
  {
    key = substr(buffer, 0, keylen);
    buffer = 0;
  }
}

BEGIN{
  buffer = 0;
  key = 0;
}
{
  if($1 ~ /[0-9]+:[0-9]+:[0-9]+\.[0-9]+/)
    flushKey();
  else if($1 ~ /0x[0-9]+:/)
  {
    pos = (substr($1,0,6) + 0);
    if(pos == 32)
    {
      keylen = ("0x"$7) + 0;
      keypos = ("0x"substr($8,0,2)) + 64;
      offset = keypos % 16;
    }
    else if(pos == keypos - offset)
      buffer = substr($NF, offset+1)
    else if(buffer != 0 && pos > keypos)
    {
      buffer = buffer $NF;
      if(length(buffer) >= keylen)
        flushKey();
    }
  }

  if(key != 0)
  {
    print key;
    key = 0;
  }
}
END{
  flushKey();
}


sh instead

sh instead

#!/bin/sh

exec awk '
/pop/ {
	if(--indent < 0)
		print "AWK: indent less than zero (" indent ")" #| "cat >&2"
}

{
	i = indent
	while(i --> 0)
		printf "\t"

	print $0
}

/push/ {
	indent++
}
'

#!/bin/awk -f

/pop/ {
	if(--indent < 0)
		print "AWK: indent less than zero (" indent ")" #| "cat >&2"
}

{
	i = indent
	while(i --> 0)
		printf "\t"

	print $0
}

/push/ {
	indent++
}


space a bit uncollapsed hints

space a bit uncollapsed hints

BEGIN {
  n_matches = 0;
  line_pos = 0;
  col_pos = 0;
  COLLAPSE_HINTS = ENVIRON["COLLAPSED_HINTS"];

  HINTS[0] = "p"
  HINTS[1] = "o"
  HINTS[2] = "i"
  HINTS[3] = "u"
  HINTS[4] = "l"
  HINTS[5] = "k"
  HINTS[6] = "j"
  HINTS[7] = "t"
  HINTS[8] = "r"
  HINTS[9] = "e"
  HINTS[10] = "wj"
  HINTS[11] = "wt"
  HINTS[12] = "wr"
  HINTS[13] = "we"
  HINTS[14] = "ww"
  HINTS[15] = "wq"
  HINTS[16] = "wf"
  HINTS[17] = "wd"
  HINTS[18] = "ws"
  HINTS[19] = "wa"
  HINTS[20] = "qp"
  HINTS[21] = "qo"
  HINTS[22] = "qi"
  HINTS[23] = "qu"
  HINTS[24] = "ql"
  HINTS[25] = "qk"
  HINTS[26] = "qj"
  HINTS[27] = "qt"
  HINTS[28] = "qr"
  HINTS[29] = "qe"
  HINTS[30] = "qw"
  HINTS[31] = "qq"
  HINTS[32] = "qf"
  HINTS[33] = "qd"
  HINTS[34] = "qs"
  HINTS[35] = "qa"
  HINTS[36] = "fp"
  HINTS[37] = "fo"
  HINTS[38] = "fi"
  HINTS[39] = "fu"
  HINTS[40] = "fl"
  HINTS[41] = "fk"
  HINTS[42] = "fj"
  HINTS[43] = "ft"
  HINTS[44] = "fr"
  HINTS[45] = "fe"
  HINTS[46] = "fw"
  HINTS[47] = "fq"
  HINTS[48] = "ff"
  HINTS[49] = "fd"
  HINTS[50] = "fs"
  HINTS[51] = "fa"
  HINTS[52] = "dp"
  HINTS[53] = "do"
  HINTS[54] = "di"
  HINTS[55] = "du"
  HINTS[56] = "dl"
  HINTS[57] = "dk"
  HINTS[58] = "dj"
  HINTS[59] = "dt"
  HINTS[60] = "dr"
  HINTS[61] = "de"
  HINTS[62] = "dw"
  HINTS[63] = "dq"
  HINTS[64] = "df"
  HINTS[65] = "dd"
  HINTS[66] = "ds"
  HINTS[67] = "da"
  HINTS[68] = "sp"
  HINTS[69] = "so"
  HINTS[70] = "si"
  HINTS[71] = "su"
  HINTS[72] = "sl"
  HINTS[73] = "sk"
  HINTS[74] = "sj"
  HINTS[75] = "st"
  HINTS[76] = "sr"
  HINTS[77] = "se"
  HINTS[78] = "sw"
  HINTS[79] = "sq"
  HINTS[80] = "sf"
  HINTS[81] = "sd"
  HINTS[82] = "ss"
  HINTS[83] = "sa"
  HINTS[84] = "ap"
  HINTS[85] = "ao"
  HINTS[86] = "ai"
  HINTS[87] = "au"
  HINTS[88] = "al"
  HINTS[89] = "ak"
  HINTS[90] = "aj"
  HINTS[91] = "at"
  HINTS[92] = "ar"
  HINTS[93] = "ae"
  HINTS[94] = "aw"
  HINTS[95] = "aq"
  HINTS[96] = "af"
  HINTS[97] = "ad"
  HINTS[98] = "as"
  HINTS[99] = "aa"

  finger_patterns = ENVIRON["FINGER_PATTERNS"];

  if (COLLAPSE_HINTS) {
    hint_format = "\033[30;1;43m%s\033[0m"
    highlight_format = "\033[1;33m%s\033[0m"
    compound_format = hint_format highlight_format
  } else {
    hint_format = "\033[1;33m[%s]\033[0m"
    highlight_format = "\033[1;33m%s\033[0m"
    compound_format = highlight_format " " hint_format
  }

  hint_lookup = ""
}

{
  line = $0;
  pos = 0;
  col_pos = 0;
	col_pos_correction = 0;

	output_line = line;

  while (match(line, finger_patterns)) {
    n_matches += 1;

		hint = HINTS[n_matches - 1]
    pos += RSTART;

    col_pos = pos;

    line_match = substr(line, RSTART, RLENGTH);
    full_line_match = line_match

    if (COLLAPSE_HINTS) {
      hint_len = length(hint)
      line_match = substr(line_match, hint_len + 1, length(line_match) - hint_len);
    }

    col_pos = col_pos + col_pos_correction

    line_pos = NR;

		pre_match = substr(output_line, 0, col_pos - 1);

    if (COLLAPSE_HINTS) {
      hint_match = sprintf(compound_format, hint, line_match);
    } else {
      hint_match = sprintf(compound_format, line_match, hint);
    }

		post_match = substr(output_line, col_pos + RLENGTH, length(line) - 1);

    output_line = pre_match hint_match post_match;

    line = post_match;

    col_pos_correction += length(sprintf(highlight_format, line_match)) + length(sprintf(hint_format, hint)) - 1;

    hint_lookup = hint_lookup hint ":" full_line_match "\n"
  }

  printf "\n%s", output_line
}

END {
  print hint_lookup | "cat 1>&3"
}

BEGIN {
  n_matches = 0;
  line_pos = 0;
  col_pos = 0;
  COLLAPSE_HINTS = ENVIRON["COLLAPSED_HINTS"];

  HINTS[0] = "p"
  HINTS[1] = "o"
  HINTS[2] = "i"
  HINTS[3] = "u"
  HINTS[4] = "l"
  HINTS[5] = "k"
  HINTS[6] = "j"
  HINTS[7] = "t"
  HINTS[8] = "r"
  HINTS[9] = "e"
  HINTS[10] = "wj"
  HINTS[11] = "wt"
  HINTS[12] = "wr"
  HINTS[13] = "we"
  HINTS[14] = "ww"
  HINTS[15] = "wq"
  HINTS[16] = "wf"
  HINTS[17] = "wd"
  HINTS[18] = "ws"
  HINTS[19] = "wa"
  HINTS[20] = "qp"
  HINTS[21] = "qo"
  HINTS[22] = "qi"
  HINTS[23] = "qu"
  HINTS[24] = "ql"
  HINTS[25] = "qk"
  HINTS[26] = "qj"
  HINTS[27] = "qt"
  HINTS[28] = "qr"
  HINTS[29] = "qe"
  HINTS[30] = "qw"
  HINTS[31] = "qq"
  HINTS[32] = "qf"
  HINTS[33] = "qd"
  HINTS[34] = "qs"
  HINTS[35] = "qa"
  HINTS[36] = "fp"
  HINTS[37] = "fo"
  HINTS[38] = "fi"
  HINTS[39] = "fu"
  HINTS[40] = "fl"
  HINTS[41] = "fk"
  HINTS[42] = "fj"
  HINTS[43] = "ft"
  HINTS[44] = "fr"
  HINTS[45] = "fe"
  HINTS[46] = "fw"
  HINTS[47] = "fq"
  HINTS[48] = "ff"
  HINTS[49] = "fd"
  HINTS[50] = "fs"
  HINTS[51] = "fa"
  HINTS[52] = "dp"
  HINTS[53] = "do"
  HINTS[54] = "di"
  HINTS[55] = "du"
  HINTS[56] = "dl"
  HINTS[57] = "dk"
  HINTS[58] = "dj"
  HINTS[59] = "dt"
  HINTS[60] = "dr"
  HINTS[61] = "de"
  HINTS[62] = "dw"
  HINTS[63] = "dq"
  HINTS[64] = "df"
  HINTS[65] = "dd"
  HINTS[66] = "ds"
  HINTS[67] = "da"
  HINTS[68] = "sp"
  HINTS[69] = "so"
  HINTS[70] = "si"
  HINTS[71] = "su"
  HINTS[72] = "sl"
  HINTS[73] = "sk"
  HINTS[74] = "sj"
  HINTS[75] = "st"
  HINTS[76] = "sr"
  HINTS[77] = "se"
  HINTS[78] = "sw"
  HINTS[79] = "sq"
  HINTS[80] = "sf"
  HINTS[81] = "sd"
  HINTS[82] = "ss"
  HINTS[83] = "sa"
  HINTS[84] = "ap"
  HINTS[85] = "ao"
  HINTS[86] = "ai"
  HINTS[87] = "au"
  HINTS[88] = "al"
  HINTS[89] = "ak"
  HINTS[90] = "aj"
  HINTS[91] = "at"
  HINTS[92] = "ar"
  HINTS[93] = "ae"
  HINTS[94] = "aw"
  HINTS[95] = "aq"
  HINTS[96] = "af"
  HINTS[97] = "ad"
  HINTS[98] = "as"
  HINTS[99] = "aa"

  finger_patterns = ENVIRON["FINGER_PATTERNS"];

  if (COLLAPSE_HINTS) {
    hint_format = "\033[30;1;43m%s\033[0m"
    highlight_format = "\033[1;33m%s\033[0m"
    compound_format = hint_format highlight_format
  } else {
    hint_format = "\033[1;33m[%s]\033[0m"
    highlight_format = "\033[1;33m%s\033[0m"
    compound_format = highlight_format hint_format
  }

  hint_lookup = ""
}

{
  line = $0;
  pos = 0;
  col_pos = 0;
	col_pos_correction = 0;

	output_line = line;

  while (match(line, finger_patterns)) {
    n_matches += 1;

		hint = HINTS[n_matches - 1]
    pos += RSTART;

    col_pos = pos;

    line_match = substr(line, RSTART, RLENGTH);
    full_line_match = line_match

    if (COLLAPSE_HINTS) {
      hint_len = length(hint)
      line_match = substr(line_match, hint_len + 1, length(line_match) - hint_len);
    }

    col_pos = col_pos + col_pos_correction

    line_pos = NR;

		pre_match = substr(output_line, 0, col_pos - 1);

    if (COLLAPSE_HINTS) {
      hint_match = sprintf(compound_format, hint, line_match);
    } else {
      hint_match = sprintf(compound_format, line_match, hint);
    }

		post_match = substr(output_line, col_pos + RLENGTH, length(line) - 1);

    output_line = pre_match hint_match post_match;

    line = post_match;

    col_pos_correction += length(sprintf(highlight_format, line_match)) + length(sprintf(hint_format, hint)) - 1;

    hint_lookup = hint_lookup hint ":" full_line_match "\n"
  }

  printf "\n%s", output_line
}

END {
  print hint_lookup | "cat 1>&3"
}


kube simpler now shows % used

kube simpler now shows % used

function split_unit(used, output) {
  if ( match(used, /([0-9]+)Ki/, value_split) ) {
    output[0] = int(value_split[1])
    output[1] = "Ki"
    return 1
  } else if ( match(used, /([0-9]+)Mi/, value_split) ) {
    output[0] = int(value_split[1])
    output[1] = "Mi"
    return 1
  } else if ( match(used, /([0-9]+)Gi/, value_split) ) {
    output[0] = int(value_split[1])
    output[1] = "Gi"
    return 1
  } else if ( match(used, /([0-9]+)Ti/, value_split) ) {
    output[0] = int(value_split[1])
    output[1] = "Ti"
    return 1
  } else if ( match(used, /([0-9]+)k/, value_split) ) {
    output[0] = int(value_split[1])
    output[1] = "k"
    return 1
  } else if ( match(used, /([0-9]+)m/, value_split) ) {
    output[0] = int(value_split[1])
    output[1] = "m"
    return 1
  } else if ( match(used, /([0-9]+)/, value_split) ) {
		output[0] = int(value_split[1])
    output[1] = "" # unknown unit
    return 1
  }
  return 0
}
function to_unit(a, a_unit, b, b_unit) {
  if (a_unit == b_unit)
    return a
  if (b_unit == "Ti") {
    if (a_unit == "Ki")
      return a / 1024 / 1024 / 1024
    if (a_unit == "Mi")
      return a / 1024 / 1024
    if (a_unit == "Gi")
      return a / 1024
    if (a_unit == "")
      return a / 1024 / 1024 / 1024 / 1024
    print "unknown a unit: " a  a_unit " for b " b b_unit
    exit 1
  } else if (b_unit == "Gi") {
    if (a_unit == "Ki")
      return a / 1024 / 1024
    if (a_unit == "Mi")
      return a / 1024
    if (a_unit == "Gi")
      return a
    if (a_unit == "")
      return a / 1024 / 1024 / 1024
    print "unknown a unit: " a  a_unit " for b " b b_unit
    exit 1
  } else if (b_unit == "k") {
    if (a_unit == "")
      return a / 1000
    print "unknown a unit: " a  a_unit " for b " b b_unit
    exit 1
  }
  print "unknown b unit: " b b_unit " with a unit " a a_unit
  exit 1
}
{
  if (split_unit($2, used) && split_unit($3, hard)) {
    value = to_unit(used[0], used[1], hard[0], hard[1])
    #value = normalize_units(unit, $2, $3)
    if (value != 0) {
      percentage = value / int(hard[0]) * 100
      printf "%-55s%-20s%-20s%s\n", $1, (sprintf("%.2f", value) hard[1]), $3, sprintf("%.2f%", percentage)
    }
  } else {
    print $0
  }
}

# functions 1024 based
function normalize_Ti(used) {
  if ( match(used, /([0-9]+)Ki/, value_split) )
    return (sprintf("%.2f", int(value_split[1]) / 1024 / 1024 / 1024) "Ti")
  else if ( match(used, /([0-9]+)Mi/, value_split) )
    return (sprintf("%.2f", int(value_split[1]) / 1024 / 1024) "Ti")
  else if ( match(used, /([0-9]+)Gi/, value_split) )
    return (sprintf("%.2f", int(value_split[1]) / 1024) "Ti")
  else if ( match(used, /([0-9]+)Ti/) )
    return used
  else
    return (sprintf("%.2f", int(used) / 1024 / 1024 / 1024 / 1024) "Ti")
}
# functions 1000 based
function normalize_k(used) {
  if ( match(used, /([0-9]+)k/) )
    return used
  else if ( match(used, /([0-9]+)m/) )
    return (sprintf("%.2f", int(used) / 1000 / 1000) "k")
  else
    return (sprintf("%.2f", int(used) / 1000) "k")
}
function normalize_no_unit(used) {
  if ( match(used, /([0-9]+)m/) )
    return sprintf("%.2f", int(used) / 1000)
  else {
    return used
  }
}

# dynamic dispatch
function normalize_units(used, hard) {
  if ( match(hard, /[0-9]+Ti/) ) {
    return normalize_Ti(used)
  } else if ( match(hard, /[0-9]+k/) ) {
    return normalize_k(used)
  } else if ( match(hard, /[0-9]+/) ) {
    return normalize_no_unit(used)
  } else {
    return used
  }
}
{
  value = normalize_units($2, $3)
  if (value != 0) {
    printf "%-55s%-20s%s\n", $1, value, $3
  }
}