Skip to content

Instantly share code, notes, and snippets.

@lordnynex
Forked from pinguet62/Hexavigesimal.java
Created August 25, 2021 18:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lordnynex/4ae8c96ab6f744a336ac033c2158e1dc to your computer and use it in GitHub Desktop.
Save lordnynex/4ae8c96ab6f744a336ac033c2158e1dc to your computer and use it in GitHub Desktop.
The Hexavigesimal numeral system.

Hexavigesimal

The numeral system using the letter to represent numbers.

This system uses only Basic Latin alphabet.

Table of contents

Examples

Decimal Hexavigesimal
0 A
1 B
2 C
... ...
24 Y
25 Z
26 AA
27 AB
... ...
700 ZZ
701 AAA
702 AAB
... ...

Bijective

In other base as octal, decimal, hexadecimal, ..., the zero at the beginning of the number is unnecessary:

0 = 00 = 000 = ...
01 = 01 = 001 = ...
02 = 02 = 002 = ...
...

But in hexavigesimal there is no representation of zero: (are equivalent)

  • ∄(i,j)∈{A;...;Z}, "ij"="j"
  • for all pair of character (i,j) from (A,A) to (Z,Z), there is no pair who ij=j!
  • AAA, ABB, ..., AZZ, BAA, ..., ZAA, ..., ZZZ

Spreadsheets

Many spreadsheets, as Excel, use this representation.

But there is a offset of 1 into column index: the first cell is A1 but not A0.

package fr.pinguet62.util;
/**
* Wrapper for hexavigesimal representation and conversions.<br />
* {@code 0} = {@code "A"}<br/>
* {@code 25} = {@code "Z"}<br/>
* {@code 26} = {@code "AA"}<br/>
* {@code 27} = {@code "AB"}<br/>
* {@code 730} = {@code "ABC"}
*/
public final class Hexavigesimal implements Comparable<Hexavigesimal> {
/**
* Check if the {@code String} representation is valid.<br/>
* It must be composed by capital letters of <a href="http://en.wikipedia.org/wiki/ISO_basic_Latin_alphabet">Basic Latin alphabet</a>.
*
* @param representation
* The {@code String} representation.
* @throws IllegalArgumentException
* Invalid {@code String} representation.
*/
private static void checkRepresentation(String representation) throws IllegalArgumentException {
if ((representation == null) || !representation.matches("[A-Z]+"))
throw new IllegalArgumentException("Invalid hexavigesimal representation.");
}
/**
* Check if the {@code long} value is valid.<br/>
* It must be positive.
*
* @param value
* The {@code long} value.
* @throws IllegalArgumentException
* Invalid {@code long} value.
*/
private static void checkValue(long value) throws IllegalArgumentException {
if (value < 0)
throw new IllegalArgumentException("Unauthorized negative value.");
}
/**
* Parses the {@code String} representation.
*
* @param representation
* The {@code String} representation.
* @return The long value.
* @throws IllegalArgumentException
* Invalid {@code String} representation.
*/
public static long parse(String representation) {
checkRepresentation(representation);
int decimal = 0;
for (int index = 0; index < representation.length(); index++) {
int m = representation.charAt(index) - 'A';
if ((representation.length() != 1) && (index != (representation.length() - 1)))
m++;
int p = representation.length() - index - 1;
decimal += m * Math.pow(26, p);
}
return decimal;
}
/**
* Gets the {@code String} representation.
*
* @param value
* The {@code long} value.
* @return The {@code String} representation.
* @throws IllegalArgumentException
* Invalid {@code String} representation.
*/
public static String toString(long value) {
checkValue(value);
long decimal = value;
StringBuilder converted = new StringBuilder();
decimal++;
while (0 < decimal) {
--decimal;
converted.append((char) ('A' + (decimal % 26)));
decimal /= 26;
}
return converted.reverse().toString();
}
/**
* Gets the {@link Hexavigesimal} instance from the {@code long} value.
*
* @param value
* The {@code long} representation.
* @return The {@link Hexavigesimal} instance.
* @throws IllegalArgumentException
* Invalid {@code long} value.
*/
public static Hexavigesimal valueOf(long value) {
return new Hexavigesimal(value);
}
/**
* Gets the {@link Hexavigesimal} instance from the {@code String} representation.
*
* @param representation
* The {@code String} representation.
* @return The {@link Hexavigesimal} instance.
* @throws IllegalArgumentException
* Invalid {@code String} representation.
*/
public static Hexavigesimal valueOf(String representation) {
return Hexavigesimal.valueOf(parse(representation));
}
/** The {@code long} value. */
private final long value;
/**
* Default constructor. <br/>
* The default value is {@code 0}={@code "A"}.
*/
public Hexavigesimal() {
value = 0;
}
/**
* Constructor with {@code long} value.
*
* @param value
* The {@code long} value.
* @throws IllegalArgumentException
* Invalid {@code long} value.
*/
public Hexavigesimal(long value) {
checkValue(value);
this.value = value;
}
/**
* Constructor with {@code String} representation.
*
* @param representation
* The {@code String} representation.
* @throws IllegalArgumentException
* Invalid {@code String} representation.
*/
public Hexavigesimal(String representation) {
value = Hexavigesimal.parse(representation);
}
/**
* Compare two {@code Hexavigesimal} values by comparing their {@link Hexavigesimal#value}.
*
* @param other
* The {@code Hexavigesimal} to compare.
*/
@Override
public int compareTo(Hexavigesimal other) {
return Long.compare(value, other.value);
}
/**
* Gets the {@code long} value.
*
* @return The {@code long} value.
*/
public long longValue() {
return value;
}
/**
* Gets the {@code String} representation.
*
* @return The {@code String} representation.
*/
@Override
public String toString() {
return toString(value);
}
}
package fr.pinguet62.util;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import org.junit.Assert;
import org.junit.Test;
/** Test for {@link fr.pinguet62.util.Hexavigesimal}. */
public final class HexavigesimalTest {
/**
* Case who associates a {@code long} value and {@code String} representation.
*/
private static class Case {
/** The {@code String} representation. */
String representation;
/** The {@code long} value. */
long value;
/**
* Constructor.
*
* @param value
* The {@code long} value.
* @param representation
* The {@code String} representation.
*/
Case(long value, String representation) {
this.value = value;
this.representation = representation;
}
}
/**
* The association of {@code long} value and {@code String} representation to test.
*/
private static Collection<Case> CASES = Arrays.asList(new Case(0L, "A"),
new Case(1L, "B"), new Case(24L, "Y"), new Case(25L, "Z"),
new Case(26L, "AA"), new Case(27L, "AB"), new Case(51L, "AZ"),
new Case(52L, "BA"), new Case(53L, "BB"), new Case(700L, "ZY"),
new Case(701L, "ZZ"), new Case(702L, "AAA"), new Case(703L, "AAB"),
new Case(1433L, "BCD"));
/**
* Check that the method throw a {@code IllegalArgumentException} with invalid {@code String} representation.
*
* @param consumer
* The method to execute.
* @throws AssertionError
* If the {@code IllegalArgumentException} does not thrown.
*/
private void checkBadRepresentation(Consumer<String> consumer) {
for (String representation : Arrays.asList(null, "", "123", "abc", "A-Z"))
try {
consumer.accept(representation);
Assert.fail();
} catch (IllegalArgumentException e) {
} catch (Exception e) {
Assert.fail();
}
}
/**
* Check that the method throw a {@code IllegalArgumentException} with invalid {@code long} values.
*
* @param consumer
* The method to execute.
* @throws AssertionError
* If the {@code IllegalArgumentException} does not thrown.
*/
private void checkBadValue(Consumer<Long> consumer) {
for (Long value : Arrays.asList(Long.MIN_VALUE, -99L, -69L, -1L))
try {
consumer.accept(value);
Assert.fail();
} catch (IllegalArgumentException e) {
} catch (Exception e) {
Assert.fail();
}
}
/**
* Test for {@link fr.pinguet62.util.Hexavigesimal#compareTo(Hexavigesimal)}. <br />
* Validate the correct comparison.
*/
@Test
public void test_compareTo() {
// -1 : value1 > value2
Assert.assertTrue(Hexavigesimal.valueOf(0).compareTo(Hexavigesimal.valueOf(1)) < 0);
Assert.assertTrue(Hexavigesimal.valueOf(0).compareTo(Hexavigesimal.valueOf(2)) < 0);
Assert.assertTrue(Hexavigesimal.valueOf(1).compareTo(Hexavigesimal.valueOf(5)) < 0);
// 0 : value1 == value2
Assert.assertEquals(0, Hexavigesimal.valueOf(0).compareTo(Hexavigesimal.valueOf(0)));
Assert.assertEquals(0, Hexavigesimal.valueOf(1).compareTo(Hexavigesimal.valueOf(1)));
// +1 : value1 < value2
Assert.assertTrue(Hexavigesimal.valueOf(1).compareTo(Hexavigesimal.valueOf(0)) > 0);
Assert.assertTrue(Hexavigesimal.valueOf(2).compareTo(Hexavigesimal.valueOf(0)) > 0);
Assert.assertTrue(Hexavigesimal.valueOf(5).compareTo(Hexavigesimal.valueOf(1)) > 0);
// Sort using natural sorting
List<Hexavigesimal> values = Arrays.asList(Hexavigesimal.valueOf(1), Hexavigesimal.valueOf(2), Hexavigesimal.valueOf(0));
Collections.sort(values);
Assert.assertEquals(0, values.get(0).longValue());
Assert.assertEquals(1, values.get(1).longValue());
Assert.assertEquals(2, values.get(2).longValue());
}
/**
* Test for {@link fr.pinguet62.util.Hexavigesimal#longValue()}. <br />
* Validate the correct conversion to {@code long} value.
*/
@Test
public void test_longValue() {
for (Case entry : CASES) {
Assert.assertEquals(entry.value, new Hexavigesimal(entry.value).longValue());
Assert.assertEquals(entry.value, Hexavigesimal.valueOf(entry.value) .longValue());
Assert.assertEquals(entry.value, new Hexavigesimal(entry.representation).longValue());
Assert.assertEquals(entry.value, Hexavigesimal.valueOf(entry.representation).longValue());
}
}
/**
* Test for {@link fr.pinguet62.util.Hexavigesimal#Hexavigesimal()},
* {@link fr.pinguet62.util.Hexavigesimal#Hexavigesimal(long))} and
* {@link fr.pinguet62.util.Hexavigesimal#Hexavigesimal(String))}. <br />
* Validate the correct instantiation.
*/
@Test
public void test_new() {
// Default constructor
Hexavigesimal valueDefault = new Hexavigesimal();
Assert.assertEquals(0, valueDefault.longValue());
Assert.assertEquals("A", valueDefault.toString());
// Long value
for (Case entry : CASES) {
Hexavigesimal value = new Hexavigesimal(entry.value);
Assert.assertEquals(entry.value, value.longValue());
Assert.assertEquals(entry.representation, value.toString());
}
// String representation
for (Case entry : CASES) {
Hexavigesimal value = new Hexavigesimal(entry.representation);
Assert.assertEquals(entry.value, value.longValue());
Assert.assertEquals(entry.representation, value.toString());
}
// IllegalArgumentException
checkBadValue(Hexavigesimal::new);
checkBadRepresentation(Hexavigesimal::new);
}
/**
* Test for {@link fr.pinguet62.util.Hexavigesimal#parse(String)}. <br />
* Validate the correct conversion {@code String} to {@code long}.
*/
@Test
public void test_parse() {
for (Case entry : CASES)
Assert.assertEquals(entry.value, Hexavigesimal.parse(entry.representation));
// IllegalArgumentException
checkBadRepresentation(Hexavigesimal::parse);
}
/**
* Test for {@link fr.pinguet62.util.Hexavigesimal#toString()} and {@link fr.pinguet62.util.Hexavigesimal#toString(long)}. <br />
* Validate the correct format of {@code String} representation.
*/
@Test
public void test_toString() {
// Static method
for (Case entry : CASES)
Assert.assertEquals(entry.representation, Hexavigesimal.toString(entry.value));
// IllegalArgumentException
checkBadValue(Hexavigesimal::toString);
// Instance method
for (Case entry : CASES)
Assert.assertEquals(entry.representation, new Hexavigesimal(entry.value).toString());
}
/**
* Test for {@link fr.pinguet62.util.Hexavigesimal#valueOf(long)} and
* {@link fr.pinguet62.util.Hexavigesimal#valueOf(String)}. <br />
* Validate the correct instantiation of {@code Hexavigesimal} object.
*/
@Test
public void test_valueOf() {
// Long value
for (Case entry : CASES) {
Hexavigesimal value = Hexavigesimal.valueOf(entry.value);
Assert.assertEquals(entry.value, value.longValue());
Assert.assertEquals(entry.representation, value.toString());
}
// String representation
for (Case entry : CASES) {
Hexavigesimal value = Hexavigesimal.valueOf(entry.representation);
Assert.assertEquals(entry.value, value.longValue());
Assert.assertEquals(entry.representation, value.toString());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment