• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tamada / oinkie / 28981083608

08 Jul 2026 10:48PM UTC coverage: 72.462% (-1.8%) from 74.24%
28981083608

Pull #20

github

tamada
fix: invalid indentation
Pull Request #20: Releases/v0.3.0

1513 of 2088 relevant lines covered (72.46%)

12.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.47
/cli/cli.rs
1
use std::path::{Path, PathBuf};
2

3
pub use crate::info::BType;
4
use clap::ValueEnum;
5
use oinkie::prelude::*;
6

7
#[derive(Debug, clap::Parser)]
8
#[command(version, about)]
9
pub struct OinkieOpts {
10
    #[clap(subcommand)]
11
    pub command: OinkieCommand,
12

13
    #[clap(short, long, value_enum, default_value_t = LogLevel::Warn, value_name = "LEVEL", ignore_case = true, help = "Log level for the application")]
14
    pub level: LogLevel,
15
}
16

17
impl OinkieOpts {
18
    pub fn init(&self) -> Result<()> {
9✔
19
        unsafe {
20
            match self.level {
9✔
21
                LogLevel::Debug => std::env::set_var("RUST_LOG", "debug"),
×
22
                LogLevel::Info => std::env::set_var("RUST_LOG", "info"),
×
23
                LogLevel::Warn => std::env::set_var("RUST_LOG", "warn"),
9✔
24
                LogLevel::Error => std::env::set_var("RUST_LOG", "error"),
×
25
                LogLevel::Trace => std::env::set_var("RUST_LOG", "trace"),
×
26
                LogLevel::Off => std::env::set_var("RUST_LOG", "off"),
×
27
            }
28
        }
29
        env_logger::init();
9✔
30
        Ok(())
9✔
31
    }
9✔
32
}
33

34
#[derive(Debug, clap::Parser, ValueEnum, Clone)]
35
pub enum LogLevel {
36
    Error,
37
    Warn,
38
    Info,
39
    Debug,
40
    Trace,
41
    Off,
42
}
43

44
#[derive(Debug, clap::Parser)]
45
pub enum OinkieCommand {
46
    #[command(name="info", about = "Display information about the application")]
47
    Info,
48

49
    #[command(name="lift", about = "Lift binary files to P-code JSON files using a specified lifter")]
50
    Lift(LiftOpts),
51

52
    #[command(name="extract", about = "Extract birthmarks from a lifted binary file (JSON format)")]
53
    Extract(ExtractOpts),
54

55
    #[command(name="compare", about = "Compare birthmarks and output the similarity score")]
56
    Compare(CompareOpts),
57
    // #[command(name="execute", about = "Execute a command on the JSON files")]
58
    // Execute(ExecuteOpts),
59
    #[command(name="reaggregate", about = "Reaggregate the element-wise similarity scores and recalculate the birthmark-wise similarity score")]
60
    Reaggregate(ReaggregateOpts),
61

62
    #[command(name="run", about = "Extract birthmarks and compare them in one command")]
63
    Run(RunOpts),
64

65
    #[cfg(debug_assertions)]
66
    #[command(name="gencomp", about = "Generate completions")]
67
    GenComp,
68
}
69

70
#[derive(Debug, clap::Parser)]
71
pub struct LiftOpts {
72
    #[clap(short, long, default_value = "pcodes", value_name = "DIRECTORY", help = "Specify the directory for putting the resultant JSON files for the lifted P-code (default: './pcodes' directory)")]
73
    dest: PathBuf,
74

75
    #[clap(short = 'l', long, value_enum, default_value_t = LifterType::Ghidra, help = "Specify the lifter type")]
76
    lifter_type: LifterType,
77

78
    #[clap(short = 'H', long, value_name = "HOME", help = "Specify the path to the home directory of the lifter (e.g., GHIDRA_HOME for Ghidra). If not specified, the environment variable (e.g., GHIDRA_HOME) or default paths are searched.")]
79
    home: Option<PathBuf>,
80

81
    #[clap(short = 'i', long = "intermediate", value_name = "DIRECTORY", help = "Directory to keep intermediate files like Ghidra project directories. If not specified, a temporary directory is used and deleted.")]
82
    intermediate_dir: Option<PathBuf>,
83

84
    #[clap(long, value_name = "SCRIPT", help = "Path to a custom lifting script. Interpretation depends on the lifter type. For Ghidra, it's the path to a Java script.")]
85
    script: Option<PathBuf>,
86

87
    #[clap(short = 'S', long, default_value_t = false, help = "Skip if the resultant JSON file already exists")]
88
    skip: bool,
89

90
    #[clap(index = 1, value_name = "FILES", help = "Path to the binary or intermediate files to lift")]
91
    files: Vec<PathBuf>,
92
}
93

94
impl LiftOpts {
95
    pub fn dest(&self) -> &Path {
2✔
96
        &self.dest
2✔
97
    }
2✔
98

99
    pub fn lifter_type(&self) -> LifterType {
1✔
100
        self.lifter_type
1✔
101
    }
1✔
102

103
    pub fn home(&self) -> Option<&Path> {
2✔
104
        self.home.as_deref()
2✔
105
    }
2✔
106

107
    pub fn intermediate_dir(&self) -> Option<&Path> {
1✔
108
        self.intermediate_dir.as_deref()
1✔
109
    }
1✔
110

111
    pub fn script(&self) -> Option<&Path> {
1✔
112
        self.script.as_deref()
1✔
113
    }
1✔
114

115
    pub fn is_skip(&self) -> bool {
1✔
116
        self.skip
1✔
117
    }
1✔
118

119
    pub fn iter(&self) -> impl Iterator<Item = &PathBuf> {
2✔
120
        self.files.iter()
2✔
121
    }
2✔
122

123
    pub fn len(&self) -> usize {
1✔
124
        self.files.len()
1✔
125
    }
1✔
126
}
127

128
#[derive(Debug, clap::Parser, ValueEnum, Clone)]
129
#[clap(rename_all = "kebab-case")]
130
pub enum BinaryType {
131
    Ghidra,
132
    Llvm,
133
    BinaryNinja,
134
}
135

136
#[derive(Debug, clap::Parser)]
137
pub struct ExtractOpts {
138
    #[clap(short, long, default_value = "birthmarks", value_name = "DIRECTORY", help = "Specify the directory for putting the resultant JSON files for the extracted birthmarks (default: './birthmarks' directory)")]
139
    dest: PathBuf,
140

141
    #[clap(short, long, value_enum, value_name = "BIRTHMARK_TYPE", default_value_t = BType::OpSeq, hide_possible_values = true, ignore_case = true, help = "Type of birthmark to extract.
142
fc (Function Calls) and op (Opcode) with set, seq, and freq variants are supported.
143
For example, 'op-seq' extracts the sequence of operations as a birthmark,
144
while 'fc-freq' extracts the frequency of function calls.
145
The full birthmark types cann be found by running 'oinkie info'.")]
146
    birthmark_type: BType,
147

148
    #[clap(short = 'S', long, default_value_t = false, help = "Skip the resultant birthmark file is already exists")]
149
    skip: bool,
150

151
    #[clap(short = 'B', long, value_enum, value_name = "BINARY_TYPE", default_value_t = BinaryType::Ghidra, ignore_case = true, help = "Type of binary. Current version only supports Ghidra JSON format")]
152
    binary_type: BinaryType,
153

154
    #[clap(index = 1, value_name = "JSON_FILES", help = "Path to the JSON files to extract birthmarks from")]
155
    files: Vec<PathBuf>,
156
}
157

158
impl ExtractOpts {
159
    pub fn dest(&self) -> &Path {
3✔
160
        &self.dest
3✔
161
    }
3✔
162

163
    pub fn extractor(&self) -> Extractor {
3✔
164
        Extractor::new(self.birthmark_type.clone().into())
3✔
165
    }
3✔
166

167
    pub fn len(&self) -> usize {
3✔
168
        self.files.len()
3✔
169
    }
3✔
170

171
    pub fn is_empty(&self) -> bool {
3✔
172
        self.files.is_empty()
3✔
173
    }
3✔
174

175
    pub fn iter(&self) -> impl Iterator<Item = &PathBuf> {
3✔
176
        self.files.iter()
3✔
177
    }
3✔
178

179
    pub fn is_skip(&self) -> bool {
6✔
180
        self.skip
6✔
181
    }
6✔
182
}
183

184
#[derive(Debug, clap::Parser)]
185
pub struct ReaggregateOpts {
186
    #[clap(
187
        short = 'A', long, default_value = "hungarian", value_name = "METHOD", ignore_case = true, 
188
        help = "Specify the aggregator for combining element-wise similarity scores into a birthmark-wise similarity score.
189
Available: 
190
- hungarian  Use the Hungarian algorithm to find the optimal matching between elements of two birthmarks,
191
             maximizing the total similarity score.
192
- topn:N     For each element in the first birthmark, consider only the top N most similar elements in the
193
             second birthmark when calculating the overall similarity score. This can reduce noise from less
194
             relevant matches and focus on the most significant similarities."
195
    )]
196
    aggregator: Aggregator,
197

198
    #[clap(short, long, value_name = "RESULT.CSV", help = "Specify the result CSV file of the comparing results to reaggregate.
199
The file contains the birthmark-wise similarity score list.", default_value = "reaggregate.csv")]
200
    dest_file: PathBuf,
201

202
    #[clap(index = 1, value_name = "SCORE_DIRECTORY", help = "Path to the directory containing the element-wise similarity scores")]
203
    score_directory: PathBuf,
204
}
205

206
impl ReaggregateOpts {
207
    pub fn aggregator(&self) -> &Aggregator {
1✔
208
        &self.aggregator
1✔
209
    }
1✔
210

211
    pub fn score_directory(&self) -> &Path {
1✔
212
        &self.score_directory
1✔
213
    }
1✔
214

215
    pub fn dest_file(&self) -> &PathBuf {
1✔
216
        &self.dest_file
1✔
217
    }
1✔
218
}
219

220
#[derive(Debug, clap::Parser)]
221
pub struct CompareOpts {
222
    #[clap(short, long, value_enum, default_value_t = Algorithm::Jaccard, value_name = "ALGORITHM", ignore_case = true, help = "Specify the similarity calculation algorithm.")]
223
    algorithm: Algorithm,    
224

225
    #[clap(
226
        short = 'A', long, default_value = "hungarian", value_name = "METHOD", ignore_case = true, 
227
        help = "Specify the aggregator for combining element-wise similarity scores into a birthmark-wise similarity score.
228
Available: 
229
- hungarian  Use the Hungarian algorithm to find the optimal matching between elements of two birthmarks,
230
             maximizing the total similarity score.
231
- topn:N     For each element in the first birthmark, consider only the top N most similar elements in the
232
             second birthmark when calculating the overall similarity score. This can reduce noise from less
233
             relevant matches and focus on the most significant similarities."
234
    )]
235
    aggregator: Aggregator,
236

237
    #[clap(short, long, value_enum, default_value_t = PairingStrategy::AllAndSelf, value_name = "STRATEGY", ignore_case = true, help = "Specify the pairing strategy for comparing files.")]
238
    strategy: PairingStrategy,
239

240
    #[clap(short, long, value_name = "DIRECTORY", help = "Specify the destination directory for the comparing results", default_value = "similarities")]
241
    dest: PathBuf,
242

243
    #[clap(short = 'S', long, default_value_t = false, help = "Skip if the similarity file already exists for the pair of birthmarks")]
244
    skip: bool,
245

246
    #[clap(index = 1, value_name = "JSON_FILES", help = "Path to the birthmark JSON files to compare")]
247
    files: Vec<PathBuf>,
248
}
249

250
impl CompareOpts {
251
    pub fn dest(&self) -> &Path {
2✔
252
        &self.dest
2✔
253
    }
2✔
254

255
    pub fn comparator(&self) -> Comparator {
2✔
256
        self.algorithm.comparator()
2✔
257
    }
2✔
258

259
    pub fn iter(&self) -> Box<dyn Iterator<Item = (&PathBuf, &PathBuf)> + Send + '_> {
2✔
260
        self.strategy.pairs(&self.files)
2✔
261
    }
2✔
262

263
    pub fn aggregator(&self) -> &Aggregator {
2✔
264
        log::info!("Using {:?} as the aggregator for combining element-wise similarity scores", self.aggregator);
2✔
265
        &self.aggregator
2✔
266
    }
2✔
267

268
    pub fn compare_count(&self) -> usize {
2✔
269
        self.strategy.compare_count(&self.files)
2✔
270
    }
2✔
271

272
    pub fn is_skip(&self) -> bool {
2✔
273
        self.skip
2✔
274
    }
2✔
275
}
276

277
#[derive(Debug, clap::Parser)]
278
pub struct RunOpts {
279
    #[clap(short, long, value_enum, default_value_t = Analysis::OpSetJaccard, ignore_case = true, help = "Similarity algorithm to use")]
280
    pub(crate) analysis: Analysis,
281

282
    #[clap(short, long, value_enum, default_value_t = PairingStrategy::AllAndSelf, ignore_case = true, help = "Pairing strategy for file comparisons")]
283
    pub strategy: PairingStrategy,
284

285
    #[clap(short, long, default_value = "similarities", help = "Destination path for the output CSV file (default: 'similarities' directory")]
286
    pub(crate) dest: PathBuf,
287

288
    #[clap(
289
        short = 'A', long, default_value = "hungarian", value_name = "METHOD", ignore_case = true,
290
        help = "Specify the aggregator for combining element-wise similarity scores into a birthmark-wise similarity score.
291
Available: 
292
- hungarian  Use the Hungarian algorithm to find the optimal matching between elements of two birthmarks,
293
             maximizing the total similarity score.
294
- topn:N     For each element in the first birthmark, consider only the top N most similar elements in the
295
             second birthmark when calculating the overall similarity score. This can reduce noise from less
296
             relevant matches and focus on the most significant similarities. available topn:N or topn:all (same as topn)."
297
    )]
298
    aggregator: Aggregator,
299

300
    #[clap(short = 'S', long, default_value_t = false, help = "Skip if the similarity file already exists for the pair of birthmarks")]
301
    pub(crate) skip: bool,
302

303
    #[clap(index = 1, help = "Path to the JSON files")]
304
    pub(crate) files: Vec<PathBuf>,
305
}
306

307
impl RunOpts {
308
    pub fn dest(&self) -> &Path {
1✔
309
        &self.dest
1✔
310
    }
1✔
311

312
    pub fn analysis_type(&self) -> AnalysisType {
1✔
313
        (&self.analysis).into()
1✔
314
    }
1✔
315

316
    pub fn iter(&self) -> Box<dyn Iterator<Item = (&PathBuf, &PathBuf)> + Send + '_> {
1✔
317
        self.strategy.pairs(&self.files)
1✔
318
    }
1✔
319

320
    pub fn compare_count(&self) -> usize {
1✔
321
        self.strategy.compare_count(&self.files)
1✔
322
    }
1✔
323

324
    pub fn is_skip(&self) -> bool {
×
325
        self.skip
×
326
    }
×
327

328
    pub fn aggregator(&self) -> &Aggregator {
1✔
329
        log::info!("Using {:?} as the aggregator for combining element-wise similarity scores", self.aggregator);
1✔
330
        &self.aggregator
1✔
331
    }
1✔
332
}
333

334
#[derive(ValueEnum, Clone, Debug)]
335
#[clap(rename_all = "kebab-case")]
336
pub(crate) enum Analysis {
337
    FcFreqCosine,
338
    FcSetDice,
339
    FcFreqEuclidean,
340
    FcSetJaccard,
341
    FcSeqLevenshtein,
342
    FcSeqLcs,
343
    FcSetSimpson,
344
    FcFreqWeightedjaccard,
345

346
    OpFreqCosine,
347
    OpSetDice,
348
    OpFreqEuclidean,
349
    OpSetJaccard,
350
    OpSeqLevenshtein,
351
    OpSeqLcs,
352
    OpSetSimpson,
353
    OpFreqWeightedjaccard,
354

355
    Op1gramSetDice,
356
    Op1gramSetJaccard,
357
    Op1gramSetSimpson,
358
    Op1gramSeqLevenshtein,
359
    Op1gramSeqLcs,
360
    Op1gramFreqCosine,
361
    Op1gramFreqEuclidean,
362
    Op1gramFreqWeightedjaccard,
363

364
    Op2gramSetDice,
365
    Op2gramSetJaccard,
366
    Op2gramSetSimpson,
367
    Op2gramSeqLevenshtein,
368
    Op2gramSeqLcs,
369
    Op2gramFreqCosine,
370
    Op2gramFreqEuclidean,
371
    Op2gramFreqWeightedjaccard,
372

373
    Op3gramSetDice,
374
    Op3gramSetJaccard,
375
    Op3gramSetSimpson,
376
    Op3gramSeqLevenshtein,
377
    Op3gramSeqLcs,
378
    Op3gramFreqCosine,
379
    Op3gramFreqEuclidean,
380
    Op3gramFreqWeightedjaccard,
381

382
    Op4gramSetDice,
383
    Op4gramSetJaccard,
384
    Op4gramSetSimpson,
385
    Op4gramSeqLevenshtein,
386
    Op4gramSeqLcs,
387
    Op4gramFreqCosine,
388
    Op4gramFreqEuclidean,
389
    Op4gramFreqWeightedjaccard,
390

391
    Op5gramSetDice,
392
    Op5gramSetJaccard,
393
    Op5gramSetSimpson,
394
    Op5gramSeqLevenshtein,
395
    Op5gramSeqLcs,
396
    Op5gramFreqCosine,
397
    Op5gramFreqEuclidean,
398
    Op5gramFreqWeightedjaccard,
399

400
    Op6gramSetDice,
401
    Op6gramSetJaccard,
402
    Op6gramSetSimpson,
403
    Op6gramSeqLevenshtein,
404
    Op6gramSeqLcs,
405
    Op6gramFreqCosine,
406
    Op6gramFreqEuclidean,
407
    Op6gramFreqWeightedjaccard,
408
}
409

410
impl From<Analysis> for AnalysisType {
411
    fn from(v: Analysis) -> Self {
×
412
        AnalysisType::from(&v)
×
413
    }
×
414
}
415

416
impl From<&Analysis> for AnalysisType {
417
    fn from(value: &Analysis) -> Self {
1✔
418
        match value {
1✔
419
            Analysis::FcFreqCosine => AnalysisType::new(BirthmarkType::FcFreq, Algorithm::Cosine),
×
420
            Analysis::FcSetDice => AnalysisType::new(BirthmarkType::FcSet, Algorithm::Dice),
×
421
            Analysis::FcFreqEuclidean => AnalysisType::new(BirthmarkType::FcFreq, Algorithm::Euclidean),
×
422
            Analysis::FcSetJaccard => AnalysisType::new(BirthmarkType::FcSet, Algorithm::Jaccard),
×
423
            Analysis::FcSeqLevenshtein => AnalysisType::new(BirthmarkType::FcSeq, Algorithm::Levenshtein),
×
424
            Analysis::FcSeqLcs => AnalysisType::new(BirthmarkType::FcSeq, Algorithm::Lcs),
×
425
            Analysis::FcSetSimpson => AnalysisType::new(BirthmarkType::FcSet, Algorithm::Simpson),
×
426
            Analysis::FcFreqWeightedjaccard => AnalysisType::new(BirthmarkType::FcFreq, Algorithm::WeightedJaccard),
×
427

428
            Analysis::OpFreqCosine => AnalysisType::new(BirthmarkType::OpFreq, Algorithm::Cosine),
×
429
            Analysis::OpSetDice => AnalysisType::new(BirthmarkType::OpSet, Algorithm::Dice),
×
430
            Analysis::OpFreqEuclidean => AnalysisType::new(BirthmarkType::OpFreq, Algorithm::Euclidean),
×
431
            Analysis::OpSetJaccard => AnalysisType::new(BirthmarkType::OpSet, Algorithm::Jaccard),
1✔
432
            Analysis::OpSeqLevenshtein => AnalysisType::new(BirthmarkType::OpSeq, Algorithm::Levenshtein),
×
433
            Analysis::OpSeqLcs => AnalysisType::new(BirthmarkType::OpSeq, Algorithm::Lcs),
×
434
            Analysis::OpSetSimpson => AnalysisType::new(BirthmarkType::OpSet, Algorithm::Simpson),
×
435
            Analysis::OpFreqWeightedjaccard => AnalysisType::new(BirthmarkType::OpFreq, Algorithm::WeightedJaccard),
×
436

437
            Analysis::Op1gramSetDice => AnalysisType::new(BirthmarkType::OpKgramSet(1), Algorithm::Dice),
×
438
            Analysis::Op1gramSetJaccard => AnalysisType::new(BirthmarkType::OpKgramSet(1), Algorithm::Jaccard),
×
439
            Analysis::Op1gramSetSimpson => AnalysisType::new(BirthmarkType::OpKgramSet(1), Algorithm::Simpson),
×
440
            Analysis::Op1gramSeqLevenshtein => AnalysisType::new(BirthmarkType::OpKgramSeq(1), Algorithm::Levenshtein),
×
441
            Analysis::Op1gramSeqLcs => AnalysisType::new(BirthmarkType::OpKgramSeq(1), Algorithm::Lcs),
×
442
            Analysis::Op1gramFreqCosine => AnalysisType::new(BirthmarkType::OpKgramFreq(1), Algorithm::Cosine),
×
443
            Analysis::Op1gramFreqEuclidean => AnalysisType::new(BirthmarkType::OpKgramFreq(1), Algorithm::Euclidean),
×
444
            Analysis::Op1gramFreqWeightedjaccard => AnalysisType::new(BirthmarkType::OpKgramFreq(1), Algorithm::WeightedJaccard),
×
445

446
            Analysis::Op2gramSetDice => AnalysisType::new(BirthmarkType::OpKgramSet(2), Algorithm::Dice),
×
447
            Analysis::Op2gramSetJaccard => AnalysisType::new(BirthmarkType::OpKgramSet(2), Algorithm::Jaccard),
×
448
            Analysis::Op2gramSetSimpson => AnalysisType::new(BirthmarkType::OpKgramSet(2), Algorithm::Simpson),
×
449
            Analysis::Op2gramSeqLevenshtein => AnalysisType::new(BirthmarkType::OpKgramSeq(2), Algorithm::Levenshtein),
×
450
            Analysis::Op2gramSeqLcs => AnalysisType::new(BirthmarkType::OpKgramSeq(2), Algorithm::Lcs),
×
451
            Analysis::Op2gramFreqCosine => AnalysisType::new(BirthmarkType::OpKgramFreq(2), Algorithm::Cosine),
×
452
            Analysis::Op2gramFreqEuclidean => AnalysisType::new(BirthmarkType::OpKgramFreq(2), Algorithm::Euclidean),
×
453
            Analysis::Op2gramFreqWeightedjaccard => AnalysisType::new(BirthmarkType::OpKgramFreq(2), Algorithm::WeightedJaccard),
×
454

455
            Analysis::Op3gramSetDice => AnalysisType::new(BirthmarkType::OpKgramSet(3), Algorithm::Dice),
×
456
            Analysis::Op3gramSetJaccard => AnalysisType::new(BirthmarkType::OpKgramSet(3), Algorithm::Jaccard),
×
457
            Analysis::Op3gramSetSimpson => AnalysisType::new(BirthmarkType::OpKgramSet(3), Algorithm::Simpson),
×
458
            Analysis::Op3gramSeqLevenshtein => AnalysisType::new(BirthmarkType::OpKgramSeq(3), Algorithm::Levenshtein),
×
459
            Analysis::Op3gramSeqLcs => AnalysisType::new(BirthmarkType::OpKgramSeq(3), Algorithm::Lcs),
×
460
            Analysis::Op3gramFreqCosine => AnalysisType::new(BirthmarkType::OpKgramFreq(3), Algorithm::Cosine),
×
461
            Analysis::Op3gramFreqEuclidean => AnalysisType::new(BirthmarkType::OpKgramFreq(3), Algorithm::Euclidean),
×
462
            Analysis::Op3gramFreqWeightedjaccard => AnalysisType::new(BirthmarkType::OpKgramFreq(3), Algorithm::WeightedJaccard),
×
463

464
            Analysis::Op4gramSetDice => AnalysisType::new(BirthmarkType::OpKgramSet(4), Algorithm::Dice),
×
465
            Analysis::Op4gramSetJaccard => AnalysisType::new(BirthmarkType::OpKgramSet(4), Algorithm::Jaccard),
×
466
            Analysis::Op4gramSetSimpson => AnalysisType::new(BirthmarkType::OpKgramSet(4), Algorithm::Simpson),
×
467
            Analysis::Op4gramSeqLevenshtein => AnalysisType::new(BirthmarkType::OpKgramSeq(4), Algorithm::Levenshtein),
×
468
            Analysis::Op4gramSeqLcs => AnalysisType::new(BirthmarkType::OpKgramSeq(4), Algorithm::Lcs),
×
469
            Analysis::Op4gramFreqCosine => AnalysisType::new(BirthmarkType::OpKgramFreq(4), Algorithm::Cosine),
×
470
            Analysis::Op4gramFreqEuclidean => AnalysisType::new(BirthmarkType::OpKgramFreq(4), Algorithm::Euclidean),
×
471
            Analysis::Op4gramFreqWeightedjaccard => AnalysisType::new(BirthmarkType::OpKgramFreq(4), Algorithm::WeightedJaccard),
×
472

473
            Analysis::Op5gramSetDice => AnalysisType::new(BirthmarkType::OpKgramSet(5), Algorithm::Dice),
×
474
            Analysis::Op5gramSetJaccard => AnalysisType::new(BirthmarkType::OpKgramSet(5), Algorithm::Jaccard),
×
475
            Analysis::Op5gramSetSimpson => AnalysisType::new(BirthmarkType::OpKgramSet(5), Algorithm::Simpson),
×
476
            Analysis::Op5gramSeqLevenshtein => AnalysisType::new(BirthmarkType::OpKgramSeq(5), Algorithm::Levenshtein),
×
477
            Analysis::Op5gramSeqLcs => AnalysisType::new(BirthmarkType::OpKgramSeq(5), Algorithm::Lcs),
×
478
            Analysis::Op5gramFreqCosine => AnalysisType::new(BirthmarkType::OpKgramFreq(5), Algorithm::Cosine),
×
479
            Analysis::Op5gramFreqEuclidean => AnalysisType::new(BirthmarkType::OpKgramFreq(5), Algorithm::Euclidean),
×
480
            Analysis::Op5gramFreqWeightedjaccard => AnalysisType::new(BirthmarkType::OpKgramFreq(5), Algorithm::WeightedJaccard),
×
481

482
            Analysis::Op6gramSetDice => AnalysisType::new(BirthmarkType::OpKgramSet(6), Algorithm::Dice),
×
483
            Analysis::Op6gramSetJaccard => AnalysisType::new(BirthmarkType::OpKgramSet(6), Algorithm::Jaccard),
×
484
            Analysis::Op6gramSetSimpson => AnalysisType::new(BirthmarkType::OpKgramSet(6), Algorithm::Simpson),
×
485
            Analysis::Op6gramSeqLevenshtein => AnalysisType::new(BirthmarkType::OpKgramSeq(6), Algorithm::Levenshtein),
×
486
            Analysis::Op6gramSeqLcs => AnalysisType::new(BirthmarkType::OpKgramSeq(6), Algorithm::Lcs),
×
487
            Analysis::Op6gramFreqCosine => AnalysisType::new(BirthmarkType::OpKgramFreq(6), Algorithm::Cosine),
×
488
            Analysis::Op6gramFreqEuclidean => AnalysisType::new(BirthmarkType::OpKgramFreq(6), Algorithm::Euclidean),
×
489
            Analysis::Op6gramFreqWeightedjaccard => AnalysisType::new(BirthmarkType::OpKgramFreq(6), Algorithm::WeightedJaccard),
×
490

491
        }
492
    }
1✔
493
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc