Custom Base6 Number

Is there a way to define a single digit base6 number that can only be the decimal values 0-5?
Or a different way to ask. Is there a way to limit a decimal value in the range of 0-5 such that num :Base6= 6 would give a compiler error?

My ultimate goal is to have [3]Base6 definition, where each position in the array is limited to range 0-5

I’ve made an attempt with bit_fields, but the best I can get is base4 or base 8. The reasons why make total sense to me. Just wondering if there is an approach others use to define custom base numbers without using strings.

Base4 :: bit_field u8 {
	num: u8 | 2
}
Base8 :: bit_field u8 {
	num: u8 | 3
}

base4: Base4
base4.num = 3

Plus I’d also have to reference the “num” name, which I’d also like to drop if possible.

I am aware of how to do a base2 to base6 or base10 to base6 conversion with a procedure. What I’m trying to do is get a more granular definition, so that my procedure does not have to return a false bool if a number input is outside the range 0-5. I’d like to push that error to the compiler if possible so that language servers can pick it up while coding.

The simplest and dumbest thing I can think of:

Base6 :: enum u8 {
	_0,
	_1,
	_2,
	_3,
	_4,
	_5,
}

x: Base6
x = ._5
1 Like

Haha, I like the idea. This is something that did not occur to me. It is quite simple and beautifully dumb. I’d actually use it, if it didn’t also come with a syntactic beat down of punishment for being so bold. To use it in an array like I wanted. I’d have to do 1 of the following. Not exactly intuitive if you’re trying to make something you hope others will use.

base6: [3]Base6
base6 = {Base6(5),Base6(5),Base6(5)}
//	or
base6 = {._5, ._5, ._5}

I realize that there is no count of bits which will align evenly with a base6 number, thus it is not likely possible. Often, my head gets stuck in the realm of math, and when I try to follow Alice to the programming worlds’ tea party, I find I’m the odd ball at the table, expecting something magical not even possible in all of Wonderland. Silly me.

In other words, I always have this feeling that there are math approaches that I’ve somehow missed over the years, and that others smarter than me (of which I am certain is many) have figured out.

So… like this?

base6 :: #force_inline proc($n: u8) -> Base6
	where 0 <= n && n < 6
{
	return cast(Base6)n
}

xs : [3]Base6 = { base6(5), base6(5), base6(5) }
1 Like

I like where your head is at. It’s still the same thing as my first example. It’s just changed the case of B to b.

Here’s the thing…
An 8bit color, is so called because there are 0-255 colors, the full range of an 8bit number. The first 16 numbers, is reserved for system colors 0-15. The last range of colors 232-255 is reserved for grayscale.

Now this is where it gets interesting at least for me. The remaining range of values, 16-231, create a 6x6x6 color cube in the same exact way 24bit colors create a 256x256x256 color cube.

So, if you shift the color range 16-231, by subtracting 16, you have 0-215. Now it is possible to work with 8bit colors in the conventional rgb way, where r, g, and b are all base 6 numbers.

Full red is rgb := {5, 0, 0}
Full green is rgb := {0, 5, 0}
Full blue is rgb := {0, 0, 5}

Currently I have

rgb666_to_8bit :: proc(rgb666: [3]u8) -> (color: u8, valid: bool) {
if rgb666.r > 5 || rgb666.g > 5 || rgb666.b > 5 {
	return 16, false // If invalid input, return black(the first color) and false
}
//	Base-6 to u8 conversion +16 since main colors are 16-231
return (rgb666.r * 36) + (rgb666.g * 6) + rgb666.b + 16, true
}

I’m considering the following change due to limitations of creating a base6 number

rgb666_to_8bit :: proc(rgb666: [3]u8) -> (color: u8, truncated: bool) #optional_ok {
	_rgb666 := rgb666
	for c, i in _rgb666 {
		if c > 5 {
			_rgb666[i] = 5
			truncated = true
		}
	}
	color = (_rgb666.r * 36) + (_rgb666.g * 6) + _rgb666.b + 16
	return
}
1 Like