use crate::error::CharacterParseError;
use crate::prelude::{ALPHABET, ALPHABET_LEN};
use nalgebra::DMatrix;
use num_integer::gcd;
#[cfg(feature = "python-integration")]
use pyo3::prelude::*;
#[derive(Debug)]
pub enum HillError {
InvalidKey,
InvalidFiller,
InvalidDirection,
CharacterParseError(CharacterParseError),
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "python-integration", pyclass(get_all))]
pub enum HillDirection {
Vertical,
Horizontal,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "python-integration", pyclass(get_all))]
pub struct Hill {
pub key: Vec<Vec<usize>>,
pub filler: char,
pub direction: HillDirection,
}
#[cfg(not(feature = "python-integration"))]
impl Hill {
pub fn new(key: Vec<Vec<usize>>, filler: char, direction: HillDirection) -> Self {
Self {
key,
filler,
direction,
}
}
pub(crate) fn is_valid_key(&self) -> bool {
let size = self.key.len();
if size == 0
|| self
.key
.iter()
.any(|row| row.len() != size || row.iter().any(|&elem| elem >= *ALPHABET_LEN))
{
return false;
}
let matrix = DMatrix::from_fn(size, size, |i, j| self.key[i][j] as f64);
let determinant = matrix.determinant().round() as isize;
gcd(determinant.abs(), (*ALPHABET_LEN).try_into().unwrap()) == 1
}
pub(crate) fn is_valid_filler(&self) -> bool {
if ALPHABET.contains(self.filler) {
return true;
}
false
}
}
#[cfg(feature = "python-integration")]
mod python_integration {
use super::*;
use crate::Traits::{Decrypt, Encrypt};
use pyo3::{prelude::*, pyclass, pymethods, PyResult};
use rand::prelude::SliceRandom;
use std::collections::HashMap;
#[pymethods]
impl Hill {
#[new]
pub fn new(key: Vec<Vec<usize>>, filler: char, direction: HillDirection) -> Self {
Self {
key,
filler,
direction,
}
}
pub(crate) fn is_valid_key(&self) -> bool {
let size = self.key.len();
if size == 0
|| self
.key
.iter()
.any(|row| row.len() != size || row.iter().any(|&elem| elem >= *ALPHABET_LEN))
{
return false;
}
let matrix = DMatrix::from_fn(size, size, |i, j| self.key[i][j] as f64);
let determinant = matrix.determinant().round() as isize;
gcd(determinant.abs(), (*ALPHABET_LEN).try_into().unwrap()) == 1
}
pub(crate) fn is_valid_filler(&self) -> bool {
if ALPHABET.contains(self.filler) {
return true;
}
false
}
pub fn encrypt(&self, input: String) -> PyResult<String> {
match Encrypt::encrypt(self, input) {
Ok(s) => Ok(s),
Err(e) => Err(pyo3::exceptions::PyException::new_err(format!("{:?}", e))),
}
}
pub fn decrypt(&self, input: String) -> PyResult<String> {
match Decrypt::decrypt(self, input) {
Ok(s) => Ok(s),
Err(e) => Err(pyo3::exceptions::PyException::new_err(format!("{:?}", e))),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::crypto_systems::hill_crypto::HillDirection;
#[test]
fn valid_key_returns_true() {
let key = vec![vec![5, 17, 6], vec![2, 21, 14], vec![19, 3, 11]];
let hill = Hill::new(key, 'x', HillDirection::Vertical);
assert!(hill.is_valid_key());
}
#[test]
fn invalid_key_returns_false() {
let key = vec![vec![6, 24, 1], vec![13, 16, 10], vec![20, 17, 30]];
let hill = Hill::new(key, 'x', HillDirection::Vertical);
assert!(!hill.is_valid_key());
}
#[test]
fn non_square_key_returns_false() {
let key = vec![vec![6, 24, 1], vec![13, 16, 10]];
let hill = Hill::new(key, 'x', HillDirection::Vertical);
assert!(!hill.is_valid_key());
}
#[test]
fn valid_filler_returns_true() {
let key = vec![vec![6, 24, 1], vec![13, 16, 10], vec![20, 17, 15]];
let hill = Hill::new(key, 'x', HillDirection::Vertical);
assert!(hill.is_valid_filler());
}
#[test]
fn invalid_filler_returns_false() {
let key = vec![vec![6, 24, 1], vec![13, 16, 10], vec![20, 17, 15]];
let hill = Hill::new(key, '1', HillDirection::Vertical);
assert!(!hill.is_valid_filler());
}
}