use crate::sparsehash;
use crate::utils;
use std::collections::HashSet;
use std::io::{Error, ErrorKind};
pub struct Index<'db> {
blocks: usize,
codes: &'db [u64],
tables: Vec<sparsehash::Table>,
masks: Vec<u64>,
begs: Vec<usize>,
}
impl Index<'_> {
pub fn new<'db>(codes: &'db [u64]) -> Result<Index, Error> {
let blocks = (64.0 / (codes.len() as f64).log2()).round() as usize;
if blocks < 2 {
Index::new_with_blocks(codes, 2)
} else {
Index::new_with_blocks(codes, blocks)
}
}
pub fn new_with_blocks<'db>(codes: &'db [u64], blocks: usize) -> Result<Index, Error> {
if codes.is_empty() {
let e = Error::new(ErrorKind::InvalidInput, "codes must not be empty.");
return Err(e);
}
if (u32::max_value() as usize) < codes.len() {
let e = Error::new(ErrorKind::InvalidInput, "number of codes must be no more than 2^32.");
return Err(e);
}
if blocks < 2 || 64 < blocks {
let e = Error::new(ErrorKind::InvalidInput, "blocks must be in [2,64].");
return Err(e);
}
let mut masks = vec![0 as u64; blocks];
let mut begs = vec![0 as usize; blocks + 1];
for b in 0..blocks {
let dim = (b + 64) / blocks;
masks[b] = (1 << dim) - 1;
begs[b + 1] = begs[b] + dim;
}
let mut tables = Vec::<sparsehash::Table>::with_capacity(blocks);
for b in 0..blocks {
let beg = begs[b];
let dim = begs[b + 1] - begs[b];
let mut table = sparsehash::Table::new(dim)?;
for id in 0..codes.len() {
let pos = (codes[id] >> beg) & masks[b];
table.count_insert(pos as usize);
}
for id in 0..codes.len() {
let pos = (codes[id] >> beg) & masks[b];
table.data_insert(pos as usize, id as u32);
}
tables.push(table);
}
Ok(Index {
blocks: blocks,
codes: codes,
tables: tables,
masks: masks,
begs: begs,
})
}
pub fn range_search(&self, qcode: u64, radius: usize) -> Vec<usize> {
let mut answers = Vec::<usize>::with_capacity(1 << 10);
self.range_search_with_buf(qcode, radius, &mut answers);
answers
}
pub fn range_search_with_buf(&self, qcode: u64, radius: usize, answers: &mut Vec<usize>) {
answers.clear();
let blocks = self.get_blocks();
let mut siggen = SigGenerator::default();
for b in 0..blocks {
if b + radius + 1 < blocks {
continue;
}
let rad = (b + radius + 1 - blocks) / blocks;
let dim = self.get_dim(b);
let qcd = self.get_chunk(qcode, b);
let table = &self.tables[b];
for r in 0..rad + 1 {
siggen.init(qcd, dim, r);
while siggen.has_next() {
let sig = siggen.next();
if let Some(a) = table.access(sig as usize) {
for v in a {
answers.push(*v as usize);
}
}
}
}
}
let mut n = 0;
if !answers.is_empty() {
answers.sort();
for i in 0..answers.len() {
if i == 0 || answers[i - 1] != answers[i] {
let dist = utils::hamdist(qcode, self.codes[answers[i]]);
if dist <= radius {
answers[n] = answers[i];
n += 1;
}
}
}
}
answers.resize(n, Default::default());
}
pub fn topk_search(&self, qcode: u64, topk: usize) -> Vec<usize> {
let mut answers = Vec::<usize>::new();
self.topk_search_with_buf(qcode, topk, &mut answers);
answers
}
pub fn topk_search_with_buf(&self, qcode: u64, topk: usize, answers: &mut Vec<usize>) {
answers.resize(65 * topk, Default::default());
let blocks = self.get_blocks();
let mut siggen = SigGenerator::default();
let mut n = 0;
let mut r = 0;
let mut counts = vec![0 as usize; 65];
let mut checked = HashSet::<usize>::new();
while n < topk {
for b in 0..blocks {
let dim = self.get_dim(b);
let qcd = self.get_chunk(qcode, b);
let table = &self.tables[b];
siggen.init(qcd, dim, r);
while siggen.has_next() {
let sig = siggen.next();
if let Some(a) = table.access(sig as usize) {
for v in a {
let id = *v as usize;
if checked.insert(id) {
let dist = utils::hamdist(qcode, self.codes[id]);
if counts[dist] < topk {
answers[dist * topk + counts[dist]] = id;
}
counts[dist] += 1;
}
}
}
}
n += counts[r * blocks + b];
if topk <= n {
break;
}
}
r += 1;
}
n = 0;
r = 0;
while n < topk {
let mut i = 0;
while i < counts[r] && n < topk {
answers[n] = answers[r * topk + i];
i += 1;
n += 1;
}
r += 1;
}
answers.resize(topk, Default::default());
}
fn get_blocks(&self) -> usize {
self.blocks
}
fn get_dim(&self, b: usize) -> usize {
self.begs[b + 1] - self.begs[b]
}
fn get_chunk(&self, code: u64, b: usize) -> u64 {
(code >> self.begs[b]) & self.masks[b]
}
}
#[derive(Clone)]
pub struct SigGenerator {
sig: u64,
base: u64,
radius: usize,
bit: isize,
power: [usize; 64],
}
impl Default for SigGenerator {
fn default() -> SigGenerator {
SigGenerator {
sig: 0,
base: 0,
radius: 0,
bit: 0,
power: [0; 64],
}
}
}
impl SigGenerator {
fn init(&mut self, base: u64, dim: usize, radius: usize) {
assert!(radius < dim);
self.sig = 0;
self.base = base;
self.radius = radius;
self.bit = radius as isize - 1;
for i in 0..radius {
self.power[i] = i;
}
self.power[radius] = dim + 1;
}
fn has_next(&self) -> bool {
self.bit != self.radius as isize
}
fn next(&mut self) -> u64 {
assert!(self.has_next());
while self.bit != -1 {
let idx = self.bit as usize;
if self.power[idx] == idx {
self.sig ^= 1 << self.power[idx];
} else {
assert!(0 < self.power[idx]);
self.sig ^= 3 << (self.power[idx] - 1);
}
self.power[idx] += 1;
self.bit -= 1;
}
let tmp = self.sig;
loop {
self.bit += 1;
let idx = self.bit as usize;
if idx >= self.radius || self.power[idx] + 1 != self.power[idx + 1] {
break;
}
assert!(0 < self.power[idx]);
self.sig ^= 1 << (self.power[idx] - 1);
self.power[idx] = idx;
}
tmp ^ self.base
}
}
#[cfg(test)]
mod tests {
use crate::ls;
use crate::mih::*;
use std::collections::BTreeSet;
fn naive_topk_search(codes: &[u64], qcode: u64, topk: usize) -> Vec<usize> {
let mut cands = ls::exhaustive_search(codes, qcode);
cands.sort_by_key(|x| x.1);
let max_dist = cands[topk - 1].1;
let mut i = 0;
let mut answers = Vec::<usize>::new();
while cands[i].1 <= max_dist {
answers.push(cands[i].0);
i += 1;
}
answers
}
#[test]
fn range_search_works() {
let codes = utils::random_codes(10000);
let index = Index::new(&codes).unwrap();
for rad in 0..6 {
for qi in (0..10000).step_by(100) {
let qcode = codes[qi];
let ans1 = ls::range_search(&codes, qcode, rad);
let ans2 = index.range_search(qcode, rad);
assert_eq!(ans1, ans2);
}
}
}
#[test]
fn topk_search_works() {
let codes = utils::random_codes(10000);
let index = Index::new(&codes).unwrap();
for topk in &[1, 10, 100] {
for qi in (0..10000).step_by(100) {
let qcode = codes[qi];
let ans1 = naive_topk_search(&codes, qcode, *topk);
let ans2 = index.topk_search(qcode, *topk);
let set1: BTreeSet<usize> = ans1.into_iter().collect();
let set2: BTreeSet<usize> = ans2.into_iter().collect();
assert_eq!(set2.is_subset(&set1), true);
}
}
}
#[test]
fn siggen_works() {
let mut siggen = SigGenerator::default();
for k in 1..5 {
siggen.init(0, 32, k);
while siggen.has_next() {
let sig = siggen.next();
assert_eq!(sig.count_ones(), k as u32);
}
}
}
}