Skip to content

Commit 27d33c9

Browse files
committed
add linear basis
1 parent 5fc9818 commit 27d33c9

File tree

5 files changed

+348
-0
lines changed

5 files changed

+348
-0
lines changed

math/linear_basis/LinearBasis.mbt

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
///|
2+
struct LinearBasis {
3+
basis : Array[Int64]
4+
max_bits : Int
5+
}
6+
7+
///|
8+
/// Construct an empty linear basis over XOR with a fixed bit width.
9+
///
10+
/// # Parameters
11+
/// - `max_bits`: maximum number of bits considered for input values.
12+
///
13+
/// # Returns
14+
/// - `LinearBasis`: an empty XOR basis ready for insertions.
15+
///
16+
/// # Example
17+
/// ```
18+
/// let basis = LinearBasis::new(60)
19+
/// inspect(basis.dimension(), content="0")
20+
/// ```
21+
pub fn LinearBasis::new(max_bits : Int) -> LinearBasis {
22+
if max_bits <= 0 {
23+
abort("max_bits must be positive")
24+
}
25+
let basis = Array::make(max_bits, 0L)
26+
{ basis, max_bits }
27+
}
28+
29+
///|
30+
/// Insert a value into the linear basis if it is linearly independent.
31+
///
32+
/// # Parameters
33+
/// - `self`: current basis.
34+
/// - `value`: value to be added.
35+
///
36+
/// # Returns
37+
/// - `Bool`: `true` when the value expands the basis, `false` otherwise.
38+
///
39+
/// # Example
40+
/// ```
41+
/// let basis = LinearBasis::new(5)
42+
/// inspect(basis.insert(3L), content="true")
43+
/// inspect(basis.insert(3L), content="false")
44+
/// ```
45+
pub fn insert(self : LinearBasis, value : Int64) -> Bool {
46+
let mut x : Int64 = value
47+
for idx in 0..<self.max_bits {
48+
let bit = self.max_bits - 1 - idx
49+
let shifted = bit
50+
if ((x >> shifted) & 1L) == 0L {
51+
continue
52+
}
53+
if self.basis[bit] == 0L {
54+
self.basis[bit] = x
55+
return true
56+
}
57+
x = x ^ self.basis[bit]
58+
}
59+
false
60+
}
61+
62+
///|
63+
/// Reduce a value with the current basis to its canonical representative.
64+
///
65+
/// # Parameters
66+
/// - `self`: current basis.
67+
/// - `value`: value to be reduced.
68+
///
69+
/// # Returns
70+
/// - `Int64`: the reduced value after eliminating basis components.
71+
///
72+
/// # Example
73+
/// ```
74+
/// let basis = LinearBasis::new(5)
75+
/// let _ = basis.insert(3L)
76+
/// inspect(basis.reduce(3L), content="0")
77+
/// ```
78+
pub fn reduce(self : LinearBasis, value : Int64) -> Int64 {
79+
let mut x = value
80+
for idx in 0..<self.max_bits {
81+
let bit = self.max_bits - 1 - idx
82+
let shifted = bit
83+
if ((x >> shifted) & 1L) == 0L {
84+
continue
85+
}
86+
if self.basis[bit] != 0L {
87+
x = x ^ self.basis[bit]
88+
}
89+
}
90+
x
91+
}
92+
93+
///|
94+
/// Query the maximum achievable XOR value with an optional seed.
95+
///
96+
/// # Parameters
97+
/// - `self`: current basis.
98+
/// - `seed`: initial value to enhance. Use `0` for the pure basis maximum.
99+
///
100+
/// # Returns
101+
/// - `Int64`: the maximal XOR obtainable with the stored basis vectors.
102+
///
103+
/// # Example
104+
/// ```
105+
/// let basis = LinearBasis::new(5)
106+
/// let _ = basis.insert(3L)
107+
/// let _ = basis.insert(6L)
108+
/// inspect(basis.max_xor(0L), content="6")
109+
/// ```
110+
pub fn max_xor(self : LinearBasis, seed : Int64) -> Int64 {
111+
let mut result = seed
112+
for idx in 0..<self.max_bits {
113+
let bit = self.max_bits - 1 - idx
114+
let candidate = result ^ self.basis[bit]
115+
if candidate > result {
116+
result = candidate
117+
}
118+
}
119+
result
120+
}
121+
122+
///|
123+
/// Count the number of basis vectors currently stored.
124+
///
125+
/// # Parameters
126+
/// - `self`: current basis.
127+
///
128+
/// # Returns
129+
/// - `Int`: the rank (dimension) of the basis.
130+
///
131+
/// # Example
132+
/// ```
133+
/// let basis = LinearBasis::new(5)
134+
/// let _ = basis.insert(1L)
135+
/// let _ = basis.insert(2L)
136+
/// let _ = basis.insert(3L)
137+
/// inspect(basis.dimension(), content="2")
138+
/// ```
139+
pub fn dimension(self : LinearBasis) -> Int {
140+
let mut count = 0
141+
for bit in 0..<self.max_bits {
142+
if self.basis[bit] != 0L {
143+
count = count + 1
144+
}
145+
}
146+
count
147+
}
148+
149+
///|
150+
/// Check whether a value can be represented by the current basis.
151+
///
152+
/// # Parameters
153+
/// - `self`: current basis.
154+
/// - `value`: value to test.
155+
///
156+
/// # Returns
157+
/// - `Bool`: `true` if the value lies in the span of the basis, `false` otherwise.
158+
///
159+
/// # Example
160+
/// ```
161+
/// let basis = LinearBasis::new(5)
162+
/// let _ = basis.insert(5L)
163+
/// let _ = basis.insert(10L)
164+
/// inspect(basis.contains(15L), content="true")
165+
/// ```
166+
pub fn contains(self : LinearBasis, value : Int64) -> Bool {
167+
self.reduce(value) == 0L
168+
}
169+
170+
///|
171+
/// Expose the internal basis array for inspection or debugging.
172+
///
173+
/// # Parameters
174+
/// - `self`: current basis.
175+
///
176+
/// # Returns
177+
/// - `Array[Int64]`: snapshot of all stored basis vectors by leading bit.
178+
///
179+
/// # Example
180+
/// ```
181+
/// let basis = LinearBasis::new(4)
182+
/// inspect(basis.vectors().length(), content="4")
183+
/// ```
184+
pub fn vectors(self : LinearBasis) -> Array[Int64] {
185+
self.basis
186+
}
187+
188+
///|
189+
/// Query the k-th largest achievable XOR value.
190+
///
191+
/// The ordering is descending and includes the zero vector as the last element.
192+
/// For example, in a basis spanning values `{0, 1, 2, 3}`, the results are
193+
/// `k=1 -> 3`, `k=2 -> 2`, `k=3 -> 1`, and `k=4 -> 0`.
194+
///
195+
/// # Parameters
196+
/// - `self`: current basis.
197+
/// - `k`: 1-indexed rank of the desired value as an `Int`. Must satisfy `1 <= k <= 2^rank`.
198+
///
199+
/// # Returns
200+
/// - `Int64`: the k-th largest value representable by the basis.
201+
///
202+
/// # Example
203+
/// ```
204+
/// let basis = LinearBasis::new(5)
205+
/// let _ = basis.insert(1L)
206+
/// let _ = basis.insert(2L)
207+
/// inspect(basis.kth_largest(1), content="3")
208+
/// inspect(basis.kth_largest(3), content="1")
209+
/// ```
210+
pub fn kth_largest(self : LinearBasis, k : Int) -> Int64 {
211+
let vectors = LinearBasis::canonical_vectors(self)
212+
let rank = vectors.length()
213+
let shift = rank
214+
if shift >= 63 {
215+
abort("basis rank too large to enumerate")
216+
}
217+
if k <= 0 {
218+
abort("k must be positive")
219+
}
220+
let total = 1L << shift
221+
let k64 = Int64::from_int(k)
222+
if k64 > total {
223+
abort("k exceeds number of representable values")
224+
}
225+
let index = total - k64
226+
let mut value = 0L
227+
for idx in 0..<rank {
228+
let vector = vectors[idx]
229+
let bit_shift = rank - 1 - idx
230+
if ((index >> bit_shift) & 1L) != 0L {
231+
value = value ^ vector
232+
}
233+
}
234+
value
235+
}
236+
237+
///|
238+
fn LinearBasis::canonical_vectors(self : LinearBasis) -> Array[Int64] {
239+
let rank = self.dimension()
240+
let ordered = Array::make(rank, 0L)
241+
let mut index = 0
242+
for idx in 0..<self.max_bits {
243+
let bit = self.max_bits - 1 - idx
244+
let value = self.basis[bit]
245+
if value != 0L {
246+
ordered[index] = value
247+
index = index + 1
248+
}
249+
}
250+
for i in 0..<rank {
251+
let mut current = ordered[i]
252+
for j in 0..<i {
253+
let candidate = current ^ ordered[j]
254+
if candidate < current {
255+
current = candidate
256+
}
257+
}
258+
ordered[i] = current
259+
for j in 0..<i {
260+
let candidate = ordered[j] ^ current
261+
if candidate < ordered[j] {
262+
ordered[j] = candidate
263+
}
264+
}
265+
}
266+
ordered
267+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
///|
2+
test "insertion and dimension" {
3+
let basis = LinearBasis::new(60)
4+
inspect(basis.insert(1L), content="true")
5+
inspect(basis.insert(2L), content="true")
6+
inspect(basis.insert(3L), content="false")
7+
inspect(basis.dimension(), content="2")
8+
}
9+
10+
///|
11+
test "reduction and containment" {
12+
let basis = LinearBasis::new(60)
13+
let _ = basis.insert(7L)
14+
let _ = basis.insert(10L)
15+
inspect(basis.contains(13L), content="true")
16+
inspect(basis.contains(5L), content="false")
17+
inspect(basis.reduce(13L), content="0")
18+
inspect(basis.reduce(4L), content="3")
19+
}
20+
21+
///|
22+
test "maximum xor queries" {
23+
let basis = LinearBasis::new(60)
24+
let _ = basis.insert(3L)
25+
let _ = basis.insert(10L)
26+
let _ = basis.insert(5L)
27+
inspect(basis.max_xor(0L), content="15")
28+
inspect(basis.max_xor(7L), content="14")
29+
inspect(basis.vectors().length(), content="60")
30+
}
31+
32+
///|
33+
test "kth largest enumeration" {
34+
let basis = LinearBasis::new(10)
35+
let _ = basis.insert(1L)
36+
let _ = basis.insert(2L)
37+
let _ = basis.insert(4L)
38+
inspect(basis.dimension(), content="3")
39+
inspect(basis.kth_largest(1), content="7")
40+
inspect(basis.kth_largest(2), content="6")
41+
inspect(basis.kth_largest(4), content="4")
42+
inspect(basis.kth_largest(7), content="1")
43+
inspect(basis.kth_largest(8), content="0")
44+
}

math/linear_basis/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Linear Basis
2+
3+
This module implements a binary linear basis for XOR operations. The structure
4+
stores one representative for every leading bit and supports:
5+
6+
- inserting values while keeping only linearly independent vectors,
7+
- reducing arbitrary numbers against the basis,
8+
- maximizing XOR results with or without a seed value,
9+
- extracting the 1-indexed k-th largest value in descending order,
10+
- checking membership and inspecting the current basis state.
11+
12+
It is useful for problems such as finding maximum subset XOR, answering online
13+
XOR queries, or verifying linear independence over GF(2).
14+

math/linear_basis/moon.pkg.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ }
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Generated using `moon info`, DON'T EDIT IT
2+
package "Lampese/Algorithms-Moonbit/math/linear_basis"
3+
4+
// Values
5+
6+
// Errors
7+
8+
// Types and methods
9+
type LinearBasis
10+
fn LinearBasis::contains(Self, Int64) -> Bool
11+
fn LinearBasis::dimension(Self) -> Int
12+
fn LinearBasis::insert(Self, Int64) -> Bool
13+
fn LinearBasis::kth_largest(Self, Int) -> Int64
14+
fn LinearBasis::max_xor(Self, Int64) -> Int64
15+
fn LinearBasis::new(Int) -> Self
16+
fn LinearBasis::reduce(Self, Int64) -> Int64
17+
fn LinearBasis::vectors(Self) -> Array[Int64]
18+
19+
// Type aliases
20+
21+
// Traits
22+

0 commit comments

Comments
 (0)