-
Notifications
You must be signed in to change notification settings - Fork 1
/
BaseCoder.scala
101 lines (90 loc) · 3.42 KB
/
BaseCoder.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/*
* Copyright (c) 2020 The National Archives
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package uk.gov.nationalarchives.oci;
import scala.annotation.tailrec
import scala.collection.immutable.List
import scala.collection.immutable.Seq;
/**
* Encodes numbers from Base10 to BaseN
* and Decodes numbers from BaseN to Base10.
*
* @author <a href="mailto:adam@evolvedbinary.com">Adam Retter</a>
*/
object BaseCoder {
/**
* Encode a Base10 integer value into BaseN.
*
* @param value the Base10 integer to encode
* @param baseN the base into which to encode the integer
*
* @return A sequence of offsets into the baseN alphabet
* for the encoded integer
*
* @throws IllegalArgumentException if {@code value} is less than zero.
*/
@throws[IllegalArgumentException]
def encode(value: BigInt, baseN: Int): Seq[Int] = {
@tailrec
def encode(v: BigInt, accum: List[Int]): List[Int] = {
if(v == 0 && accum.nonEmpty) {
accum
} else if(v <= 1) {
(v.toInt :: accum)
} else {
val div = v / baseN
val mod = v % baseN
encode(div, mod.toInt :: accum)
}
}
if (value < 0) {
throw new IllegalArgumentException("Negative values cannot be encoded")
}
encode(value, List.empty[Int])
}
/**
* Decode a BaseN string into a Base10 integer.
*
* @param str the BaseN encoded string
* @param baseN the base into which to encode the integer
* @param characterToNumericValue a function that converts an encoded BaseN char to a numeric value
* the function should return -1 if there is no possible conversion
*
* @throws IllegalArgumentException if {@code value} is less than zero.
*/
@throws[IllegalArgumentException]
def decode(str: String, baseN: Int, characterToNumericValue: Char => Int): BigInt = {
if (str.isEmpty) {
throw new IllegalArgumentException("Cannot decode empty-string")
}
val numericValues = str.map { character =>
val numericValue = characterToNumericValue(character)
if (numericValue < 0) {
throw new IllegalArgumentException(s"Character '$character' cannot be resolved to a numeric value")
}
numericValue
}
val vs: Seq[BigInt] = for(i <- 0 until numericValues.length) yield {
val exp = (numericValues.length - i) - 1
numericValues(i) * BigInt(baseN).pow(exp)
}
vs.reduceLeft(_ + _)
}
}