Split out Ranges struct
This commit is contained in:
parent
ba86c49bfb
commit
e363a552f0
@ -25,3 +25,6 @@ thiserror = "1.0.30"
|
|||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
travis-ci = { repository = "wangds/puzzle-solver" }
|
travis-ci = { repository = "wangds/puzzle-solver" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
drawille = "0.3.0"
|
||||||
|
@ -6,6 +6,7 @@ pub mod constraint;
|
|||||||
mod error;
|
mod error;
|
||||||
mod linexpr;
|
mod linexpr;
|
||||||
mod puzzle;
|
mod puzzle;
|
||||||
|
mod ranges;
|
||||||
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use num_rational::Rational32;
|
use num_rational::Rational32;
|
||||||
|
104
src/puzzle.rs
104
src/puzzle.rs
@ -1,14 +1,12 @@
|
|||||||
//! The puzzle's state and rules.
|
//! The puzzle's state and rules.
|
||||||
|
|
||||||
use bit_set::BitSet;
|
use bit_set::BitSet;
|
||||||
use ranges::GenericRange;
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops;
|
use std::ops;
|
||||||
use std::ops::Bound;
|
|
||||||
use std::ops::RangeBounds;
|
use std::ops::RangeBounds;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
@ -16,7 +14,7 @@ use crate::constraint;
|
|||||||
use crate::Error;
|
use crate::Error;
|
||||||
use crate::{Constraint, LinExpr, PsResult, Solution, Val, VarToken};
|
use crate::{Constraint, LinExpr, PsResult, Solution, Val, VarToken};
|
||||||
|
|
||||||
use ranges::Ranges;
|
use crate::ranges::Ranges;
|
||||||
|
|
||||||
/// A collection of candidates.
|
/// A collection of candidates.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
@ -24,7 +22,7 @@ enum Candidates {
|
|||||||
None, // A variable with no candidates.
|
None, // A variable with no candidates.
|
||||||
Value(Val), // A variable set to its initial value.
|
Value(Val), // A variable set to its initial value.
|
||||||
Set(Rc<BTreeSet<Val>>), // A variable with a list of candidates.
|
Set(Rc<BTreeSet<Val>>), // A variable with a list of candidates.
|
||||||
Range(Ranges<Val>), // A variable with candidate ranges.
|
Range(Ranges), // A variable with candidate ranges.
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The state of a variable during the solution search.
|
/// The state of a variable during the solution search.
|
||||||
@ -81,26 +79,7 @@ impl Candidates {
|
|||||||
Candidates::None => 0,
|
Candidates::None => 0,
|
||||||
Candidates::Value(_) => 1,
|
Candidates::Value(_) => 1,
|
||||||
Candidates::Set(ref rc) => rc.len(),
|
Candidates::Set(ref rc) => rc.len(),
|
||||||
Candidates::Range(r) => {
|
Candidates::Range(rc) => rc.len(),
|
||||||
let mut total = 0;
|
|
||||||
|
|
||||||
for i in r.as_slice() {
|
|
||||||
let min = match i.start_bound() {
|
|
||||||
Bound::Included(val) => *val,
|
|
||||||
Bound::Excluded(val) => val + 1,
|
|
||||||
Bound::Unbounded => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let max = match i.end_bound() {
|
|
||||||
Bound::Included(val) => *val,
|
|
||||||
Bound::Excluded(val) => val - 1,
|
|
||||||
Bound::Unbounded => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
total += max - min + 1;
|
|
||||||
}
|
|
||||||
total as _
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,9 +89,7 @@ impl Candidates {
|
|||||||
Candidates::None => Box::new(iter::empty()),
|
Candidates::None => Box::new(iter::empty()),
|
||||||
Candidates::Value(val) => Box::new(iter::once(*val)),
|
Candidates::Value(val) => Box::new(iter::once(*val)),
|
||||||
Candidates::Set(ref rc) => Box::new(rc.iter().cloned()),
|
Candidates::Set(ref rc) => Box::new(rc.iter().cloned()),
|
||||||
Candidates::Range(range) => {
|
Candidates::Range(range) => Box::new(range.iter()),
|
||||||
Box::new(range.as_slice().iter().map(|x| x.into_iter()).flatten())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -390,18 +367,15 @@ impl Puzzle {
|
|||||||
|
|
||||||
match self.candidates[idx] {
|
match self.candidates[idx] {
|
||||||
ref mut x @ Candidates::None => {
|
ref mut x @ Candidates::None => {
|
||||||
*x = Candidates::Range(Ranges::from((
|
*x = Candidates::Range(Ranges::new(
|
||||||
range.start_bound().cloned(),
|
|
||||||
range.end_bound().cloned(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
Candidates::Value(_) => panic!("attempt to set fixed variable"),
|
|
||||||
Candidates::Range(ref mut r) => {
|
|
||||||
r.insert(GenericRange::new_with_bounds(
|
|
||||||
range.start_bound().cloned(),
|
range.start_bound().cloned(),
|
||||||
range.end_bound().cloned(),
|
range.end_bound().cloned(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
Candidates::Value(_) => panic!("attempt to set fixed variable"),
|
||||||
|
Candidates::Range(ref mut r) => {
|
||||||
|
r.insert(range.start_bound().cloned(), range.end_bound().cloned());
|
||||||
|
}
|
||||||
Candidates::Set(_) => panic!("attempt to insert range on set"),
|
Candidates::Set(_) => panic!("attempt to insert range on set"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -664,7 +638,7 @@ impl<'a> PuzzleSearch<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator over the candidates to an unassigned variable.
|
/// Get an iterator over the candidates to an unassigned variable.
|
||||||
pub fn get_unassigned(&'a self, var: VarToken) -> Box<dyn Iterator<Item = Val> + 'a> {
|
pub fn get_unassigned(&self, var: VarToken) -> Box<dyn Iterator<Item = Val> + '_> {
|
||||||
let VarToken(idx) = var;
|
let VarToken(idx) = var;
|
||||||
match &self.vars[idx] {
|
match &self.vars[idx] {
|
||||||
VarState::Assigned(_) => Box::new(iter::empty()),
|
VarState::Assigned(_) => Box::new(iter::empty()),
|
||||||
@ -681,28 +655,7 @@ impl<'a> PuzzleSearch<'a> {
|
|||||||
VarState::Unassigned(ref cs) => match cs {
|
VarState::Unassigned(ref cs) => match cs {
|
||||||
Candidates::None => Err(Error::Default),
|
Candidates::None => Err(Error::Default),
|
||||||
Candidates::Value(val) => Ok((*val, *val)),
|
Candidates::Value(val) => Ok((*val, *val)),
|
||||||
Candidates::Range(r) => {
|
Candidates::Range(r) => r.get_bounds().ok_or(Error::Default),
|
||||||
let slice = r.as_slice();
|
|
||||||
let first = slice.get(0).ok_or(Error::Default)?.start_bound();
|
|
||||||
let last = slice
|
|
||||||
.get(slice.len() - 1)
|
|
||||||
.ok_or(Error::Default)?
|
|
||||||
.end_bound();
|
|
||||||
|
|
||||||
let min = match first {
|
|
||||||
Bound::Included(val) => *val,
|
|
||||||
Bound::Excluded(val) => val + 1,
|
|
||||||
Bound::Unbounded => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let max = match last {
|
|
||||||
Bound::Included(val) => *val,
|
|
||||||
Bound::Excluded(val) => val - 1,
|
|
||||||
Bound::Unbounded => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((min, max))
|
|
||||||
}
|
|
||||||
Candidates::Set(ref rc) => rc
|
Candidates::Set(ref rc) => rc
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
@ -726,8 +679,8 @@ impl<'a> PuzzleSearch<'a> {
|
|||||||
Candidates::None => Err(Error::Default),
|
Candidates::None => Err(Error::Default),
|
||||||
Candidates::Value(v) => bool_to_result(*v == val),
|
Candidates::Value(v) => bool_to_result(*v == val),
|
||||||
Candidates::Range(ref mut r) => {
|
Candidates::Range(ref mut r) => {
|
||||||
if r.contains(&val) {
|
if r.contains(val) {
|
||||||
*r = Ranges::from(val..=val);
|
*r = (val..=val).into();
|
||||||
self.wake.union_with(&self.constraints.wake[idx]);
|
self.wake.union_with(&self.constraints.wake[idx]);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -761,7 +714,7 @@ impl<'a> PuzzleSearch<'a> {
|
|||||||
Candidates::None => Err(Error::Default),
|
Candidates::None => Err(Error::Default),
|
||||||
Candidates::Value(v) => bool_to_result(*v != val),
|
Candidates::Value(v) => bool_to_result(*v != val),
|
||||||
Candidates::Range(r) => {
|
Candidates::Range(r) => {
|
||||||
if r.contains(&val) {
|
if r.contains(val) {
|
||||||
r.remove(val..=val);
|
r.remove(val..=val);
|
||||||
self.wake.union_with(&self.constraints.wake[idx]);
|
self.wake.union_with(&self.constraints.wake[idx]);
|
||||||
}
|
}
|
||||||
@ -808,36 +761,19 @@ impl<'a> PuzzleSearch<'a> {
|
|||||||
Err(Error::Default)
|
Err(Error::Default)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Candidates::Range(ref mut r) => {
|
Candidates::Range(ref mut r) => {
|
||||||
if max < min {
|
if max < min {
|
||||||
return Err(Error::Default);
|
return Err(Error::Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
*r = r.clone().intersect(min..=max);
|
r.intersection(min..=max);
|
||||||
|
|
||||||
self.wake.union_with(&self.constraints.wake[idx]);
|
self.wake.union_with(&self.constraints.wake[idx]);
|
||||||
|
|
||||||
let slice = r.as_slice();
|
r.get_bounds().ok_or(Error::Default)
|
||||||
let first = slice.get(0).ok_or(Error::Default)?.start_bound();
|
|
||||||
let last = slice
|
|
||||||
.get(slice.len() - 1)
|
|
||||||
.ok_or(Error::Default)?
|
|
||||||
.end_bound();
|
|
||||||
|
|
||||||
let min = match first {
|
|
||||||
Bound::Included(val) => *val,
|
|
||||||
Bound::Excluded(val) => val + 1,
|
|
||||||
Bound::Unbounded => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let max = match last {
|
|
||||||
Bound::Included(val) => *val,
|
|
||||||
Bound::Excluded(val) => val - 1,
|
|
||||||
Bound::Unbounded => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((min, max))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Candidates::Set(rc) => {
|
Candidates::Set(rc) => {
|
||||||
let &curr_min = rc.iter().min().expect("candidates");
|
let &curr_min = rc.iter().min().expect("candidates");
|
||||||
let &curr_max = rc.iter().max().expect("candidates");
|
let &curr_max = rc.iter().max().expect("candidates");
|
||||||
@ -1029,7 +965,7 @@ impl<'a> PuzzleSearch<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> fmt::Debug for PuzzleSearch<'a> {
|
impl fmt::Debug for PuzzleSearch<'_> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
write!(f, "PuzzleSearch={{")?;
|
write!(f, "PuzzleSearch={{")?;
|
||||||
for (idx, var) in self.vars.iter().enumerate() {
|
for (idx, var) in self.vars.iter().enumerate() {
|
||||||
@ -1055,7 +991,7 @@ impl<'a> fmt::Debug for PuzzleSearch<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ops::Index<VarToken> for PuzzleSearch<'a> {
|
impl ops::Index<VarToken> for PuzzleSearch<'_> {
|
||||||
type Output = Val;
|
type Output = Val;
|
||||||
|
|
||||||
/// Get the value assigned to a variable.
|
/// Get the value assigned to a variable.
|
||||||
|
121
src/ranges.rs
Normal file
121
src/ranges.rs
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
use std::ops::{Bound, Range, RangeBounds, RangeInclusive};
|
||||||
|
|
||||||
|
use ranges::GenericRange;
|
||||||
|
|
||||||
|
use crate::Val;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct Ranges {
|
||||||
|
inner: ranges::Ranges<Val>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ranges {
|
||||||
|
pub fn new(from: Bound<Val>, to: Bound<Val>) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: ranges::Ranges::from((from, to)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn intersection<R: Into<Ranges>>(&mut self, other: R) {
|
||||||
|
let left = std::mem::replace(&mut self.inner, Default::default());
|
||||||
|
self.inner = left & other.into().inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn contains(&self, val: Val) -> bool {
|
||||||
|
self.inner.contains(&val)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn remove<R: RangeBounds<Val>>(&mut self, range: R) {
|
||||||
|
self.inner.remove(GenericRange::new_with_bounds(
|
||||||
|
range.start_bound().cloned(),
|
||||||
|
range.end_bound().cloned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.inner.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
let mut total = 0;
|
||||||
|
|
||||||
|
for i in self.inner.as_slice() {
|
||||||
|
let min = match i.start_bound() {
|
||||||
|
Bound::Included(val) => *val,
|
||||||
|
Bound::Excluded(val) => val + 1,
|
||||||
|
Bound::Unbounded => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let max = match i.end_bound() {
|
||||||
|
Bound::Included(val) => *val,
|
||||||
|
Bound::Excluded(val) => val - 1,
|
||||||
|
Bound::Unbounded => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
total += max - min + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
total as _
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_bounds(&self) -> Option<(Val, Val)> {
|
||||||
|
let slice = self.inner.as_slice();
|
||||||
|
let first = slice.get(0)?.start_bound();
|
||||||
|
let last = slice.get(slice.len() - 1)?.end_bound();
|
||||||
|
|
||||||
|
let min = match first {
|
||||||
|
Bound::Included(val) => *val,
|
||||||
|
Bound::Excluded(val) => val + 1,
|
||||||
|
Bound::Unbounded => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let max = match last {
|
||||||
|
Bound::Included(val) => *val,
|
||||||
|
Bound::Excluded(val) => val - 1,
|
||||||
|
Bound::Unbounded => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Some((min, max))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn insert(&mut self, from: Bound<Val>, to: Bound<Val>) {
|
||||||
|
self.inner.insert(GenericRange::new_with_bounds(from, to));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = Val> + '_ {
|
||||||
|
self.inner
|
||||||
|
.as_slice()
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.into_iter())
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(Bound<Val>, Bound<Val>)> for Ranges {
|
||||||
|
fn from(range: (Bound<Val>, Bound<Val>)) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: range.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RangeInclusive<Val>> for Ranges {
|
||||||
|
fn from(range: RangeInclusive<Val>) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: range.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Range<Val>> for Ranges {
|
||||||
|
fn from(range: Range<Val>) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: range.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@ const FLAG_OFF: Val = 1;
|
|||||||
const FLAG_ON: Val = 2;
|
const FLAG_ON: Val = 2;
|
||||||
const FLAG_UNKNOWN: Val = 4;
|
const FLAG_UNKNOWN: Val = 4;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct Nonogram {
|
struct Nonogram {
|
||||||
vars: Vec<VarToken>,
|
vars: Vec<VarToken>,
|
||||||
rule: Vec<usize>,
|
rule: Vec<usize>,
|
||||||
@ -146,7 +147,7 @@ impl Nonogram {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Constraint for Nonogram {
|
impl Constraint for Nonogram {
|
||||||
fn vars<'a>(&'a self) -> Box<dyn Iterator<Item = &'a VarToken> + 'a> {
|
fn vars(&self) -> Box<dyn Iterator<Item = &'_ VarToken> + '_> {
|
||||||
Box::new(self.vars.iter())
|
Box::new(self.vars.iter())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,13 +204,18 @@ fn make_nonogram(rows: &[Vec<usize>], cols: &[Vec<usize>]) -> (Puzzle, Vec<Vec<V
|
|||||||
(sys, vars)
|
(sys, vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_nonogram(dict: &Solution, vars: &[Vec<VarToken>]) {
|
fn print_nonogram(dict: &Solution, w: u32, h: u32, vars: &[Vec<VarToken>]) {
|
||||||
for row in vars.iter() {
|
let mut canvas = drawille::Canvas::new(w, h);
|
||||||
for &var in row.iter() {
|
|
||||||
print!("{}", if dict[var] == 1 { "*" } else { "." });
|
for (y, row) in vars.iter().enumerate() {
|
||||||
|
for (x, &var) in row.iter().enumerate() {
|
||||||
|
if dict[var] == 1 {
|
||||||
|
canvas.set(x as _, y as _);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
println!();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("{}", canvas.frame());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_nonogram(dict: &Solution, vars: &[Vec<VarToken>], expected: &Board) {
|
fn verify_nonogram(dict: &Solution, vars: &[Vec<VarToken>], expected: &Board) {
|
||||||
@ -293,7 +299,7 @@ fn nonogram_wikipedia() {
|
|||||||
|
|
||||||
let (mut sys, vars) = make_nonogram(&puzzle_rows, &puzzle_cols);
|
let (mut sys, vars) = make_nonogram(&puzzle_rows, &puzzle_cols);
|
||||||
let dict = sys.solve_unique().expect("solution");
|
let dict = sys.solve_unique().expect("solution");
|
||||||
print_nonogram(&dict, &vars);
|
print_nonogram(&dict, puzzle_cols.len() as _, puzzle_rows.len() as _, &vars);
|
||||||
verify_nonogram(&dict, &vars, &expected);
|
verify_nonogram(&dict, &vars, &expected);
|
||||||
println!("nonogram_wikipedia: {} guesses.", sys.num_guesses());
|
println!("nonogram_wikipedia: {} guesses.", sys.num_guesses());
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,13 @@ extern crate puzzle_solver;
|
|||||||
use puzzle_solver::*;
|
use puzzle_solver::*;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct NoDiagonal {
|
struct NoDiagonal {
|
||||||
vars: Vec<VarToken>,
|
vars: Vec<VarToken>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Constraint for NoDiagonal {
|
impl Constraint for NoDiagonal {
|
||||||
fn vars<'a>(&'a self) -> Box<dyn Iterator<Item = &'a VarToken> + 'a> {
|
fn vars(&self) -> Box<dyn Iterator<Item = &'_ VarToken> + '_> {
|
||||||
Box::new(self.vars.iter())
|
Box::new(self.vars.iter())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ use std::rc::Rc;
|
|||||||
const X: Val = -1;
|
const X: Val = -1;
|
||||||
|
|
||||||
/*--------------------------------------------------------------*/
|
/*--------------------------------------------------------------*/
|
||||||
|
#[derive(Debug)]
|
||||||
struct BinaryRepr {
|
struct BinaryRepr {
|
||||||
// value = sum_i 2^i bits[i]
|
// value = sum_i 2^i bits[i]
|
||||||
value: VarToken,
|
value: VarToken,
|
||||||
@ -60,7 +60,7 @@ fn make_sums(size: usize) -> Vec<Val> {
|
|||||||
let mut v = val as usize;
|
let mut v = val as usize;
|
||||||
|
|
||||||
while v > 0 {
|
while v > 0 {
|
||||||
count += (v & 1);
|
count += v & 1;
|
||||||
v >>= 1;
|
v >>= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user