From 302a93e6dab43da6997657e6f6abd1153f172077 Mon Sep 17 00:00:00 2001 From: Zhiyong Fang Date: Sun, 3 Nov 2024 21:59:04 -0600 Subject: [PATCH] Log up Update (#40) * simple refactor * refactor * fmt * fix --- ecgo/examples/log_up/main.go | 550 ++++++++++++++++--------------- expander_compiler/tests/logup.rs | 229 +++++++++++++ go.mod | 6 +- go.sum | 2 + 4 files changed, 513 insertions(+), 274 deletions(-) create mode 100644 expander_compiler/tests/logup.rs diff --git a/ecgo/examples/log_up/main.go b/ecgo/examples/log_up/main.go index 25e8faf..0074d51 100644 --- a/ecgo/examples/log_up/main.go +++ b/ecgo/examples/log_up/main.go @@ -1,273 +1,277 @@ -package main - -import ( - "math" - "math/big" - "math/rand" - "os" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/constraint/solver" - "github.com/consensys/gnark/frontend" - - "github.com/PolyhedraZK/ExpanderCompilerCollection/ecgo" - "github.com/PolyhedraZK/ExpanderCompilerCollection/ecgo/test" -) - -type RationalNumber struct { - Numerator frontend.Variable - Denominator frontend.Variable -} - -func (r *RationalNumber) Add(api frontend.API, other *RationalNumber) RationalNumber { - return RationalNumber{ - Numerator: api.Add(api.Mul(r.Numerator, other.Denominator), api.Mul(other.Numerator, r.Denominator)), - Denominator: api.Mul(r.Denominator, other.Denominator), - } -} - -// 0 is considered a power of 2 in this case -func IsPowerOf2(n int) bool { - return n&(n-1) == 0 -} - -// Construct a binary summation tree to sum all the values -func SumRationalNumbers(api frontend.API, rs []RationalNumber) RationalNumber { - n := len(rs) - if n == 0 { - return RationalNumber{Numerator: 0, Denominator: 1} - } - - if !IsPowerOf2(n) { - panic("The length of rs should be a power of 2") - } - - cur := rs - next := make([]RationalNumber, 0) - - for n > 1 { - n >>= 1 - for i := 0; i < n; i++ { - next = append(next, cur[i*2].Add(api, &cur[i*2+1])) - } - cur = next - next = next[:0] - } - - if len(cur) != 1 { - panic("Summation code may be wrong.") - } - - return cur[0] -} - -type LogUpCircuit struct { - Table [][]frontend.Variable - QueryID []frontend.Variable - QueryResult [][]frontend.Variable -} - -func NewRandomCircuit( - n_table_rows uint, - n_queries uint, - n_columns uint, - fill_values bool, -) *LogUpCircuit { - c := &LogUpCircuit{} - c.Table = make([][]frontend.Variable, n_table_rows) - for i := 0; i < int(n_table_rows); i++ { - c.Table[i] = make([]frontend.Variable, n_columns) - if fill_values { - for j := 0; j < int(n_columns); j++ { - c.Table[i][j] = rand.Intn(math.MaxInt) - } - } - } - - c.QueryID = make([]frontend.Variable, n_queries) - c.QueryResult = make([][]frontend.Variable, n_queries) - - for i := 0; i < int(n_queries); i++ { - c.QueryResult[i] = make([]frontend.Variable, n_columns) - if fill_values { - query_id := rand.Intn(int(n_table_rows)) - c.QueryID[i] = query_id - c.QueryResult[i] = c.Table[query_id] - } - } - - return c -} - -type ColumnCombineOptions int - -const ( - Poly = iota - FullRandom -) - -func SimpleMin(a uint, b uint) uint { - if a < b { - return a - } else { - return b - } -} - -func GetColumnRandomness(api ecgo.API, n_columns uint, column_combine_options ColumnCombineOptions) []frontend.Variable { - var randomness = make([]frontend.Variable, n_columns) - if column_combine_options == Poly { - beta := api.GetRandomValue() - randomness[0] = 1 - randomness[1] = beta - - // Hopefully this will generate fewer layers than sequential pows - max_deg := uint(1) - for max_deg < n_columns { - for i := max_deg + 1; i <= SimpleMin(max_deg*2, n_columns-1); i++ { - randomness[i] = api.Mul(randomness[max_deg], randomness[i-max_deg]) - } - max_deg *= 2 - } - - // Debug Code: - // for i := 1; i < n_columns; i++ { - // api.AssertIsEqual(randomness[i], api.Mul(randomness[i - 1], beta)) - // } - - } else if column_combine_options == FullRandom { - randomness[0] = 1 - for i := 1; i < int(n_columns); i++ { - randomness[i] = api.GetRandomValue() - } - } else { - panic("Unknown poly combine options") - } - return randomness -} - -func CombineColumn(api ecgo.API, vec_2d [][]frontend.Variable, randomness []frontend.Variable) []frontend.Variable { - n_rows := len(vec_2d) - if n_rows == 0 { - return make([]frontend.Variable, 0) - } - - n_columns := len(vec_2d[0]) - - // Do not introduce any randomness - if n_columns == 1 { - vec_combined := make([]frontend.Variable, n_rows) - for i := 0; i < n_rows; i++ { - vec_combined[i] = vec_2d[i][0] - } - return vec_combined - } - - if !IsPowerOf2(n_columns) { - panic("Consider support this") - } - - vec_return := make([]frontend.Variable, 0) - for i := 0; i < n_rows; i++ { - var v_at_row_i frontend.Variable = 0 - for j := 0; j < n_columns; j++ { - v_at_row_i = api.Add(v_at_row_i, api.Mul(randomness[j], vec_2d[i][j])) - } - vec_return = append(vec_return, v_at_row_i) - } - return vec_return -} - -// TODO: Do we need bits check for the count? -func QueryCountHintFn(field *big.Int, inputs []*big.Int, outputs []*big.Int) error { - for i := 0; i < len(outputs); i++ { - outputs[i] = big.NewInt(0) - } - - for i := 0; i < len(inputs); i++ { - query_id := inputs[i].Int64() - outputs[query_id].Add(outputs[query_id], big.NewInt(1)) - } - return nil -} - -func (c *LogUpCircuit) Check(api ecgo.API, column_combine_option ColumnCombineOptions) error { - if len(c.Table) == 0 || len(c.QueryID) == 0 { - panic("empty table or empty query") - } // Should we allow this? - - // The challenge used to complete polynomial identity check - alpha := api.GetRandomValue() - - column_combine_randomness := GetColumnRandomness(api, uint(len(c.Table[0])), column_combine_option) - - // Table Polynomial - table_single_column := CombineColumn(api, c.Table, column_combine_randomness) - query_count, _ := api.NewHint( - QueryCountHintFn, - len(c.Table), - c.QueryID..., - ) - - table_poly := make([]RationalNumber, len(table_single_column)) - for i := 0; i < len(table_single_column); i++ { - table_poly[i] = RationalNumber{ - Numerator: query_count[i], - Denominator: api.Sub(alpha, table_single_column[i]), - } - } - table_poly_at_alpha := SumRationalNumbers(api, table_poly) - - // Query Polynomial - query_single_column := CombineColumn(api, c.QueryResult, column_combine_randomness) - query_poly := make([]RationalNumber, len(query_single_column)) - for i := 0; i < len(query_single_column); i++ { - query_poly[i] = RationalNumber{ - Numerator: 1, - Denominator: api.Sub(alpha, query_single_column[i]), - } - } - query_poly_at_alpha := SumRationalNumbers(api, query_poly) - - api.AssertIsEqual( - api.Mul(table_poly_at_alpha.Numerator, query_poly_at_alpha.Denominator), - api.Mul(query_poly_at_alpha.Numerator, table_poly_at_alpha.Denominator), - ) - return nil -} - -const ColumnCombineOption ColumnCombineOptions = FullRandom - -// Define declares the circuit's constraints -func (c *LogUpCircuit) Define(api frontend.API) error { - return c.Check(api.(ecgo.API), ColumnCombineOption) -} - -func main() { - N_TABLE_ROWS := uint(128) - N_QUERIES := uint(512) - COLUMN_SIZE := uint(8) - - circuit, err := ecgo.Compile(ecc.BN254.ScalarField(), NewRandomCircuit(N_TABLE_ROWS, N_QUERIES, COLUMN_SIZE, false)) - if err != nil { - panic(err.Error()) - } - - c := circuit.GetLayeredCircuit() - os.WriteFile("circuit.txt", c.Serialize(), 0o644) - - assignment := NewRandomCircuit(N_TABLE_ROWS, N_QUERIES, COLUMN_SIZE, true) - solver.RegisterHint(QueryCountHintFn) - inputSolver := circuit.GetInputSolver() - witness, err := inputSolver.SolveInput(assignment, 0) - if err != nil { - panic(err.Error()) - } - - if !test.CheckCircuit(c, witness) { - panic("Circuit not satisfied") - } - - // os.WriteFile("inputsolver.txt", inputSolver.Serialize(), 0o644) - os.WriteFile("witness.txt", witness.Serialize(), 0o644) -} +package main + +import ( + "math" + "math/rand" + "os" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/frontend" + + "github.com/PolyhedraZK/ExpanderCompilerCollection/ecgo" + "github.com/PolyhedraZK/ExpanderCompilerCollection/ecgo/test" +) + +type RationalNumber struct { + Numerator frontend.Variable + Denominator frontend.Variable +} + +func (r *RationalNumber) Add(api frontend.API, other *RationalNumber) RationalNumber { + return RationalNumber{ + Numerator: api.Add(api.Mul(r.Numerator, other.Denominator), api.Mul(other.Numerator, r.Denominator)), + Denominator: api.Mul(r.Denominator, other.Denominator), + } +} + +// Construct a binary summation tree to sum all the values +func SumRationalNumbers(api frontend.API, vs []RationalNumber) RationalNumber { + n := len(vs) + if n == 0 { + return RationalNumber{Numerator: 0, Denominator: 1} + } + + vvs := make([]RationalNumber, len(vs)) + copy(vvs, vs) + + n_values_to_sum := len(vvs) + for n_values_to_sum > 1 { + half_size_floor := n_values_to_sum / 2 + for i := 0; i < half_size_floor; i++ { + vvs[i] = vvs[i].Add(api, &vvs[i+half_size_floor]) + } + + if n_values_to_sum&1 != 0 { + vvs[half_size_floor] = vvs[n_values_to_sum-1] + } + + n_values_to_sum = (n_values_to_sum + 1) / 2 + } + + return vvs[0] +} + +type LogUpCircuit struct { + TableKeys [][]frontend.Variable + TableValues [][]frontend.Variable + QueryKeys [][]frontend.Variable + QueryResult [][]frontend.Variable + + QueryCount []frontend.Variable +} + +func NewRandomCircuit( + key_len uint, + n_table_rows uint, + n_queries uint, + n_columns uint, + fill_values bool, +) *LogUpCircuit { + c := &LogUpCircuit{} + + c.QueryCount = make([]frontend.Variable, n_table_rows) + if fill_values { + for i := 0; i < int(n_table_rows); i++ { + c.QueryCount[i] = uint(0) + } + } + + c.TableKeys = make([][]frontend.Variable, n_table_rows) + for i := 0; i < int(n_table_rows); i++ { + c.TableKeys[i] = make([]frontend.Variable, key_len) + if fill_values { + for j := 0; j < int(key_len); j++ { + c.TableKeys[i][j] = rand.Intn(math.MaxInt) + } + } + } + + c.TableValues = make([][]frontend.Variable, n_table_rows) + for i := 0; i < int(n_table_rows); i++ { + c.TableValues[i] = make([]frontend.Variable, n_columns) + if fill_values { + for j := 0; j < int(n_columns); j++ { + c.TableValues[i][j] = rand.Intn(math.MaxInt) + } + } + } + + c.QueryKeys = make([][]frontend.Variable, n_queries) + c.QueryResult = make([][]frontend.Variable, n_queries) + + for i := 0; i < int(n_queries); i++ { + c.QueryKeys[i] = make([]frontend.Variable, key_len) + c.QueryResult[i] = make([]frontend.Variable, n_columns) + if fill_values { + query_id := rand.Intn(int(n_table_rows)) + c.QueryKeys[i] = c.TableKeys[query_id] + c.QueryResult[i] = c.TableValues[query_id] + c.QueryCount[query_id] = c.QueryCount[query_id].(uint) + 1 + } + } + + return c +} + +type ColumnCombineOptions int + +const ( + Poly = iota + FullRandom +) + +func SimpleMin(a uint, b uint) uint { + if a < b { + return a + } else { + return b + } +} + +func GetColumnRandomness(api ecgo.API, n_columns uint, column_combine_options ColumnCombineOptions) []frontend.Variable { + var randomness = make([]frontend.Variable, n_columns) + if column_combine_options == Poly { // not tested yet, don't use + beta := api.GetRandomValue() + randomness[0] = 1 + randomness[1] = beta + + // Hopefully this will generate fewer layers than sequential pows + max_deg := uint(1) + for max_deg < n_columns { + for i := max_deg + 1; i <= SimpleMin(max_deg*2, n_columns-1); i++ { + randomness[i] = api.Mul(randomness[max_deg], randomness[i-max_deg]) + } + max_deg *= 2 + } + + // Debug Code: + // for i := 1; i < n_columns; i++ { + // api.AssertIsEqual(randomness[i], api.Mul(randomness[i - 1], beta)) + // } + + } else if column_combine_options == FullRandom { + randomness[0] = 1 + for i := 1; i < int(n_columns); i++ { + randomness[i] = api.GetRandomValue() + } + } else { + panic("Unknown poly combine options") + } + return randomness +} + +func CombineColumn(api ecgo.API, vec_2d [][]frontend.Variable, randomness []frontend.Variable) []frontend.Variable { + n_rows := len(vec_2d) + if n_rows == 0 { + return make([]frontend.Variable, 0) + } + + n_columns := len(vec_2d[0]) + if n_columns != len(randomness) { + panic("Inconsistent randomness length and column size") + } + + vec_return := make([]frontend.Variable, 0) + for i := 0; i < n_rows; i++ { + var v_at_row_i frontend.Variable = 0 + for j := 0; j < n_columns; j++ { + v_at_row_i = api.Add(v_at_row_i, api.Mul(randomness[j], vec_2d[i][j])) + } + vec_return = append(vec_return, v_at_row_i) + } + return vec_return +} + +func LogUpPolyValsAtAlpha(api ecgo.API, vec_1d []frontend.Variable, count []frontend.Variable, x frontend.Variable) RationalNumber { + poly := make([]RationalNumber, len(vec_1d)) + for i := 0; i < len(vec_1d); i++ { + poly[i] = RationalNumber{ + Numerator: count[i], + Denominator: api.Sub(x, vec_1d[i]), + } + } + return SumRationalNumbers(api, poly) +} + +func CombineVecAt2d(a [][]frontend.Variable, b [][]frontend.Variable) [][]frontend.Variable { + if len(a) != len(b) { + panic("Length does not match at combine 2d") + } + + r := make([][]frontend.Variable, len(a)) + for i := 0; i < len(a); i++ { + for j := 0; j < len(a[i]); j++ { + r[i] = append(r[i], a[i][j]) + } + + for j := 0; j < len(b[i]); j++ { + r[i] = append(r[i], b[i][j]) + } + } + + return r +} + +func (c *LogUpCircuit) Check(api ecgo.API, column_combine_option ColumnCombineOptions) error { + + // The challenge used to complete polynomial identity check + alpha := api.GetRandomValue() + // The randomness used to combine the columns + column_combine_randomness := GetColumnRandomness(api, uint(len(c.TableKeys[0])+len(c.TableValues[0])), column_combine_option) + + // Table Polynomial + table_combined := CombineVecAt2d(c.TableKeys, c.TableValues) + table_single_column := CombineColumn(api, table_combined, column_combine_randomness) + table_poly_at_alpha := LogUpPolyValsAtAlpha(api, table_single_column, c.QueryCount, alpha) + + // Query Polynomial + query_combined := CombineVecAt2d(c.QueryKeys, c.QueryResult) + query_single_column := CombineColumn(api, query_combined, column_combine_randomness) + dummy_count := make([]frontend.Variable, len(query_single_column)) + for i := 0; i < len(dummy_count); i++ { + dummy_count[i] = 1 + } + query_poly_at_alpha := LogUpPolyValsAtAlpha(api, query_single_column, dummy_count, alpha) + + api.AssertIsEqual( + api.Mul(table_poly_at_alpha.Numerator, query_poly_at_alpha.Denominator), + api.Mul(query_poly_at_alpha.Numerator, table_poly_at_alpha.Denominator), + ) + return nil +} + +const ColumnCombineOption ColumnCombineOptions = FullRandom + +// Define declares the circuit's constraints +func (c *LogUpCircuit) Define(api frontend.API) error { + return c.Check(api.(ecgo.API), ColumnCombineOption) +} + +func main() { + KEY_LEN := uint(8) + N_TABLE_ROWS := uint(128) + N_QUERIES := uint(512) + COLUMN_SIZE := uint(8) + + circuit, err := ecgo.Compile(ecc.BN254.ScalarField(), NewRandomCircuit(KEY_LEN, N_TABLE_ROWS, N_QUERIES, COLUMN_SIZE, false)) + if err != nil { + panic(err.Error()) + } + + c := circuit.GetLayeredCircuit() + os.WriteFile("circuit.txt", c.Serialize(), 0o644) + + assignment := NewRandomCircuit(KEY_LEN, N_TABLE_ROWS, N_QUERIES, COLUMN_SIZE, true) + inputSolver := circuit.GetInputSolver() + witness, err := inputSolver.SolveInput(assignment, 0) + if err != nil { + panic(err.Error()) + } + + if !test.CheckCircuit(c, witness) { + panic("Circuit not satisfied") + } + + // os.WriteFile("inputsolver.txt", inputSolver.Serialize(), 0o644) + os.WriteFile("witness.txt", witness.Serialize(), 0o644) +} diff --git a/expander_compiler/tests/logup.rs b/expander_compiler/tests/logup.rs new file mode 100644 index 0000000..fc32d44 --- /dev/null +++ b/expander_compiler/tests/logup.rs @@ -0,0 +1,229 @@ +use arith::Field; +use expander_compiler::frontend::*; +use extra::Serde; +use rand::{thread_rng, Rng}; + +const KEY_LEN: usize = 3; +const N_TABLE_ROWS: usize = 17; +const N_COLUMNS: usize = 5; +const N_QUERIES: usize = 33; + +declare_circuit!(Circuit { + table_keys: [[Variable; KEY_LEN]; N_TABLE_ROWS], + table_values: [[Variable; N_COLUMNS]; N_TABLE_ROWS], + + query_keys: [[Variable; KEY_LEN]; N_QUERIES], + query_results: [[Variable; N_COLUMNS]; N_QUERIES], + + // counting the number of occurences for each row of the table + query_count: [Variable; N_TABLE_ROWS], +}); + +#[derive(Clone, Copy)] +struct Rational { + numerator: Variable, + denominator: Variable, +} + +fn add_rational(builder: &mut API, v1: &Rational, v2: &Rational) -> Rational { + let p1 = builder.mul(v1.numerator, v2.denominator); + let p2 = builder.mul(v1.denominator, v2.numerator); + + Rational { + numerator: builder.add(p1, p2), + denominator: builder.mul(v1.denominator, v2.denominator), + } +} + +fn assert_eq_rational(builder: &mut API, v1: &Rational, v2: &Rational) { + let p1 = builder.mul(v1.numerator, v2.denominator); + let p2 = builder.mul(v1.denominator, v2.numerator); + builder.assert_is_equal(p1, p2); +} + +fn sum_rational_vec(builder: &mut API, vs: &[Rational]) -> Rational { + if vs.is_empty() { + return Rational { + numerator: builder.constant(0), + denominator: builder.constant(1), + }; + } + + // Basic version: + // let mut sum = Rational { + // numerator: builder.constant(0), + // denominator: builder.constant(1), + // }; + // for i in 0..vs.len() { + // sum = add_rational(builder, &sum, &vs[i]); + // } + // sum + + // Fewer-layers version: + let mut vvs = vs.to_owned(); + let mut n_values_to_sum = vvs.len(); + while n_values_to_sum > 1 { + let half_size_floor = n_values_to_sum / 2; + for i in 0..half_size_floor { + vvs[i] = add_rational(builder, &vvs[i], &vvs[i + half_size_floor]) + } + + if n_values_to_sum & 1 != 0 { + vvs[half_size_floor] = vvs[n_values_to_sum - 1]; + } + + n_values_to_sum = (n_values_to_sum + 1) / 2; + } + vvs[0] +} + +// TODO: Add poly randomness +fn get_column_randomness(builder: &mut API, n_columns: usize) -> Vec { + let mut randomness = vec![]; + randomness.push(builder.constant(1)); + for _ in 1..n_columns { + randomness.push(builder.get_random_value()); + } + randomness +} + +fn combine_columns( + builder: &mut API, + vec_2d: &Vec>, + randomness: &[Variable], +) -> Vec { + if vec_2d.is_empty() { + return vec![]; + } + + let column_size = vec_2d[0].len(); + assert!(randomness.len() == column_size); + vec_2d + .iter() + .map(|row| { + row.iter() + .zip(randomness) + .fold(builder.constant(0), |acc, (v, r)| { + let prod = builder.mul(v, r); + builder.add(acc, prod) + }) + }) + .collect() +} + +fn logup_poly_val( + builder: &mut API, + vals: &[Variable], + counts: &[Variable], + x: &Variable, +) -> Rational { + let poly_terms = vals + .iter() + .zip(counts) + .map(|(v, c)| Rational { + numerator: *c, + denominator: builder.sub(x, v), + }) + .collect::>(); + sum_rational_vec(builder, &poly_terms) +} + +impl Define for Circuit { + fn define(&self, builder: &mut API) { + let alpha = builder.get_random_value(); + let randomness = get_column_randomness(builder, KEY_LEN + N_COLUMNS); + + let table_combined = combine_columns( + builder, + &self + .table_keys + .iter() + .zip(self.table_values) + .map(|(row_key, row_value)| [row_key.to_vec(), row_value.to_vec()].concat()) + .collect(), + &randomness, + ); + let v_table = logup_poly_val(builder, &table_combined, &self.query_count, &alpha); + + let query_combined = combine_columns( + builder, + &self + .query_keys + .iter() + .zip(self.query_results) + .map(|(row_key, row_value)| [row_key.to_vec(), row_value.to_vec()].concat()) + .collect(), + &randomness, + ); + let one = builder.constant(1); + let v_query = logup_poly_val( + builder, + &query_combined, + &vec![one; query_combined.len()], + &alpha, + ); + + assert_eq_rational(builder, &v_table, &v_query); + } +} + +#[inline] +fn gen_assignment() -> Circuit { + let mut circuit = Circuit::::default(); + let mut rng = thread_rng(); + for i in 0..N_TABLE_ROWS { + for j in 0..KEY_LEN { + circuit.table_keys[i][j] = C::CircuitField::random_unsafe(&mut rng); + } + + for j in 0..N_COLUMNS { + circuit.table_values[i][j] = C::CircuitField::random_unsafe(&mut rng); + } + } + + circuit.query_count = [C::CircuitField::ZERO; N_TABLE_ROWS]; + for i in 0..N_QUERIES { + let query_id: usize = rng.gen::() % N_TABLE_ROWS; + circuit.query_count[query_id] += C::CircuitField::ONE; + circuit.query_keys[i] = circuit.table_keys[query_id]; + circuit.query_results[i] = circuit.table_values[query_id]; + } + + circuit +} + +fn logup_test_helper() { + let compile_result: CompileResult = compile(&Circuit::default()).unwrap(); + let assignment = gen_assignment::(); + let witness = compile_result + .witness_solver + .solve_witness(&assignment) + .unwrap(); + let output = compile_result.layered_circuit.run(&witness); + assert_eq!(output, vec![true]); + + let file = std::fs::File::create("circuit.txt").unwrap(); + let writer = std::io::BufWriter::new(file); + compile_result + .layered_circuit + .serialize_into(writer) + .unwrap(); + + let file = std::fs::File::create("witness.txt").unwrap(); + let writer = std::io::BufWriter::new(file); + witness.serialize_into(writer).unwrap(); + + let file = std::fs::File::create("witness_solver.txt").unwrap(); + let writer = std::io::BufWriter::new(file); + compile_result + .witness_solver + .serialize_into(writer) + .unwrap(); +} + +#[test] +fn logup_test() { + logup_test_helper::(); + logup_test_helper::(); + logup_test_helper::(); +} diff --git a/go.mod b/go.mod index 62cf241..a96b272 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,11 @@ require ( github.com/consensys/gnark-crypto v0.13.0 ) -require github.com/kr/text v0.2.0 // indirect +require ( + github.com/fxamacker/cbor/v2 v2.5.0 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/x448/float16 v0.8.4 // indirect +) require ( github.com/bits-and-blooms/bitset v1.13.0 // indirect diff --git a/go.sum b/go.sum index 2634771..9577628 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE= github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=