function _color_hash_djb2
{
	local c h s
	s=$1
	h=${2:-5381}
	while (( ${#s} > 0 )); do
		c=0x${s%${s#??}}
		s=${s#??}
		h=$(( ((($h << 5) + $h) ^ $c) & 0xffff ))
	done
	print -nr -- $h
}

function _color_hash_fnv1
{
	local c h s
	s=$1
	h=${2:-2166136261}
	while (( ${#s} > 0 )); do
		c=0x${s%${s#??}}
		s=${s#??}
		h=$(( (($h << 24) + ($h << 8) +
		    ($h << 7) + ($h << 4) + ($h << 1) + $h) & 0xffffffff ))
		h=$(( $h ^ $c ))
	done
	# xor-fold to 16 bits
	print -nr -- $(( ($h >> 16) ^ ($h & 0xffff) ))
}

function _color_hash_fnv1a
{
	local c h s
	s=$1
	h=${2:-2166136261}
	while (( ${#s} > 0 )); do
		c=0x${s%${s#??}}
		s=${s#??}
		h=$(( $h ^ $c ))
		h=$(( (($h << 24) + ($h << 8) +
		    ($h << 7) + ($h << 4) + ($h << 1) + $h) & 0xffffffff ))
	done
	# xor-fold to 16 bits
	print -nr -- $(( ($h >> 16) ^ ($h & 0xffff) ))
}

function _color_hash_sha
{
	local c h s p
	p=$1
	shift
	s=$(printf "%08x%s" "${2:-0}" "$1")
	h=$("$p" -qs "$s")
	c=$((0x${h#${h%?}}))
	while (( $c > 0 )); do
		h=${h#??}
		c=$(($c - 1))
	done
	h=$((0x${h%${h#????}}))
	print -nr -- $h
}

function _color_hash_sha1
{
	_color_hash_sha sha1 "$@"
}

function _color_hash_sha256
{
	_color_hash_sha sha256 "$@"
}

function _color_hash_sha512
{
	_color_hash_sha sha512 "$@"
}

function color_hash
{
	local bright impl flag h n s

	bright=false
	impl=_color_hash_fnv1a
	while getopts bi: flag; do
		case $flag in
		b)	bright=true ;;
		i)	if [[ $OPTARG != @(djb2|fnv1|fnv1a|sha1|sha256|sha512) ]]; then
				return 1
			fi
			impl=_color_hash_$OPTARG
			;;
		*)	return 1 ;;
		esac
	done
	shift $(($OPTIND - 1))

	h=
	s=$(print -nr -- "$1" | od -A n -t x1 | tr -d '[:space:]*')
	while :; do
		h=$($impl "$s" $h)
		# Avoid modulo bias.
		(( $h >= 4 )) && break
	done
	h=$(( $h % 12 ))

	if $bright; then
		# $h < 6 ? 8 + 1 + $h : 8 + 1 + $h - 6
		n=$(( $h + ($h < 6 ? 9 : 3) ))
	else
		# $h < 6 ? 1 + $h : 8 + 1 + $h - 6
		n=$(( $h + ($h < 6 ? 1 : 3) ))
	fi

	print -nr -- "$n"
}
