Compare commits
53 Commits
final
...
4lab-final
| Author | SHA1 | Date | |
|---|---|---|---|
| a5b624a333 | |||
| 4bcec8060d | |||
| 4982ace7a6 | |||
| 14d853c878 | |||
| de4bfa4f84 | |||
| 25a5646093 | |||
| 9e3e5cda0c | |||
| a3061e6678 | |||
| f05a39da06 | |||
| 7ccdb8346e | |||
| 1aa359b5c1 | |||
| 69e7f5537a | |||
| 2ad9d5d784 | |||
| 9f7e34f7cd | |||
| ddabccfd2b | |||
| bfa7c918b2 | |||
| b816f9f17c | |||
| d8db6809e4 | |||
| 09b722a8eb | |||
| 66279d0ff4 | |||
| 1ea541e8c4 | |||
| 377cc25109 | |||
| 3065249023 | |||
| ae0bc951aa | |||
| 2097a70cbf | |||
| 33c029ce7f | |||
| 2781fbabbc | |||
| 8c75ccf7ef | |||
| 1d7ceb8fc9 | |||
| 842bffc8cd | |||
| 622224db79 | |||
| b9cf0e7fe3 | |||
| e90dfd6816 | |||
| 0faffa20ce | |||
| 19b8202fa8 | |||
| 01003c1ee0 | |||
| 2dedc4416a | |||
| 3205247f1c | |||
| 54e266b899 | |||
| 5711244be4 | |||
| e6d636cff2 | |||
| abb078e1b6 | |||
| d9076f7dfa | |||
| a69a81d89a | |||
| f1fd3c1dea | |||
| 2dc18efbe4 | |||
| ef9f0448e4 | |||
| 098db8a7f0 | |||
| 035f2baf76 | |||
| bb1dcb815f | |||
| a4ed564c62 | |||
| 40b467cba5 | |||
| 7b452ed326 |
BIN
coursework/task.jpg
Normal file
|
After Width: | Height: | Size: 187 KiB |
3
lab3/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.stack-work/
|
||||
*~
|
||||
!*.txt
|
||||
11
lab3/CHANGELOG.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Changelog for `lab3`
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to the
|
||||
[Haskell Package Versioning Policy](https://pvp.haskell.org/).
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 0.1.0.0 - YYYY-MM-DD
|
||||
26
lab3/LICENSE
Normal file
@@ -0,0 +1,26 @@
|
||||
Copyright 2024 Author name here
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
1
lab3/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# lab3
|
||||
2
lab3/Setup.hs
Normal file
@@ -0,0 +1,2 @@
|
||||
import Distribution.Simple
|
||||
main = defaultMain
|
||||
83
lab3/app/Main.hs
Normal file
@@ -0,0 +1,83 @@
|
||||
module Main (main) where
|
||||
|
||||
import Text.Read (readMaybe)
|
||||
import Codec.Picture
|
||||
import qualified Data.Vector.Unboxed as VU
|
||||
import Lib
|
||||
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
putStrLn "Введите значение сдвига для шифра Цезаря:"
|
||||
inputCaesarShift <- getLine
|
||||
let caesarShift = case readMaybe inputCaesarShift of
|
||||
Just value -> value
|
||||
Nothing -> error "Ожидалось целое число!"
|
||||
|
||||
putStrLn "Введите количество бит для кодирования:"
|
||||
inputBitsPerByte <- getLine
|
||||
let bitsPerByte = case readMaybe inputBitsPerByte of
|
||||
Just value -> value
|
||||
Nothing -> error "Ожидалось целое число!"
|
||||
|
||||
let sourceTextPath = "resources/biography.txt"
|
||||
let sourceImagePath = "resources/david.bmp"
|
||||
let alphabetPath = "tmp/alphabet.txt"
|
||||
let encryptedTextPath = "tmp/biography_encrypted.txt"
|
||||
let encodedImagePath = "tmp/david_" ++ show bitsPerByte ++ "_" ++ show caesarShift ++ ".bmp"
|
||||
let decodedTextPath = "tmp/biography.txt"
|
||||
|
||||
putStrLn $ "\nЧтение текста из файла \"" ++ sourceTextPath ++ "\""
|
||||
inputText <- readFile sourceTextPath
|
||||
putStrLn $ "10 символов текста: \"" ++ take 10 inputText ++ "\""
|
||||
|
||||
putStrLn "\nШифрование текста"
|
||||
let alphabet = createAlphabetFromText inputText
|
||||
putStrLn $ "Размер алфавита: " ++ show (length alphabet)
|
||||
writeFile alphabetPath alphabet
|
||||
putStrLn $ "Алфавит сохранён в файл \"" ++ alphabetPath ++ "\""
|
||||
let encryptedText = encryptCaesar alphabet caesarShift inputText
|
||||
putStrLn $ "10 символов шифра: \"" ++ take 10 encryptedText ++ "\""
|
||||
writeFile encryptedTextPath encryptedText
|
||||
putStrLn $ "Зашифрованный текст сохранён в файл \"" ++ encryptedTextPath ++ "\""
|
||||
let encryptedTextBits = textToBits encryptedText
|
||||
putStrLn $ "10 битов шифра: \"" ++ show (take 10 $ VU.toList encryptedTextBits) ++ "\""
|
||||
|
||||
putStrLn "\nКодирование текста в изображение"
|
||||
readSourceImageResult <- readImage sourceImagePath
|
||||
case readSourceImageResult of
|
||||
Left err -> putStrLn $ "Ошибка при чтении изображения: " ++ err
|
||||
Right dynImg -> do
|
||||
let img = convertRGB8 dynImg
|
||||
let width = imageWidth img
|
||||
let height = imageHeight img
|
||||
let totalBits = width * height * 3 * bitsPerByte
|
||||
let bits = encryptedTextBits VU.++ VU.replicate (totalBits - VU.length encryptedTextBits) 0
|
||||
|
||||
let resultImage = generateImage (encodePixel bitsPerByte img bits) width height
|
||||
saveBmpImage encodedImagePath (ImageRGB8 resultImage)
|
||||
putStrLn $ "Изображение сохранено по пути: \"" ++ encodedImagePath ++ "\""
|
||||
|
||||
putStrLn $ "\nЧтение алфавита из файла \"" ++ alphabetPath ++ "\""
|
||||
alphabetFromFile <- readFile alphabetPath
|
||||
|
||||
putStrLn "\nДекодирование текста из изображения"
|
||||
case extractShift encodedImagePath of
|
||||
Just extractedCaesarShift -> do
|
||||
putStrLn $ "Из названия файла извлечён ключ: " ++ show extractedCaesarShift
|
||||
|
||||
readEncodedImageResult <- readImage encodedImagePath
|
||||
case readEncodedImageResult of
|
||||
Left err -> putStrLn $ "Ошибка при чтении изображения: " ++ err
|
||||
Right dynImg -> do
|
||||
let img = convertRGB8 dynImg
|
||||
let bits = VU.fromList $ extractBitsFromImage bitsPerByte img
|
||||
putStrLn $ "10 битов шифра: \"" ++ show (take 10 $ VU.toList bits) ++ "\""
|
||||
let encryptedTextFromImage = takeWhile (/= '\NUL') (bitsToText bits)
|
||||
putStrLn $ "10 символов шифра: \"" ++ take 10 encryptedTextFromImage ++ "\""
|
||||
let decryptedText = decryptCaesar alphabetFromFile extractedCaesarShift encryptedTextFromImage
|
||||
putStrLn $ "10 символов текста: \"" ++ take 10 decryptedText ++ "\""
|
||||
writeFile decodedTextPath decryptedText
|
||||
putStrLn $ "Текст сохранён по пути: \"" ++ decodedTextPath ++ "\""
|
||||
|
||||
Nothing -> putStrLn "Не удалось извлечь ключ."
|
||||
73
lab3/lab3.cabal
Normal file
@@ -0,0 +1,73 @@
|
||||
cabal-version: 2.2
|
||||
|
||||
-- This file has been generated from package.yaml by hpack version 0.37.0.
|
||||
--
|
||||
-- see: https://github.com/sol/hpack
|
||||
|
||||
name: lab3
|
||||
version: 0.1.0.0
|
||||
description: Please see the README on GitHub at <https://github.com/githubuser/lab3#readme>
|
||||
homepage: https://github.com/githubuser/lab3#readme
|
||||
bug-reports: https://github.com/githubuser/lab3/issues
|
||||
author: Author name here
|
||||
maintainer: example@example.com
|
||||
copyright: 2024 Author name here
|
||||
license: BSD-3-Clause
|
||||
license-file: LICENSE
|
||||
build-type: Simple
|
||||
extra-source-files:
|
||||
README.md
|
||||
CHANGELOG.md
|
||||
|
||||
source-repository head
|
||||
type: git
|
||||
location: https://github.com/githubuser/lab3
|
||||
|
||||
library
|
||||
exposed-modules:
|
||||
Lib
|
||||
other-modules:
|
||||
Paths_lab3
|
||||
autogen-modules:
|
||||
Paths_lab3
|
||||
hs-source-dirs:
|
||||
src
|
||||
ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints
|
||||
build-depends:
|
||||
JuicyPixels
|
||||
, base >=4.7 && <5
|
||||
, vector
|
||||
default-language: Haskell2010
|
||||
|
||||
executable lab3-exe
|
||||
main-is: Main.hs
|
||||
other-modules:
|
||||
Paths_lab3
|
||||
autogen-modules:
|
||||
Paths_lab3
|
||||
hs-source-dirs:
|
||||
app
|
||||
ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N
|
||||
build-depends:
|
||||
JuicyPixels
|
||||
, base >=4.7 && <5
|
||||
, lab3
|
||||
, vector
|
||||
default-language: Haskell2010
|
||||
|
||||
test-suite lab3-test
|
||||
type: exitcode-stdio-1.0
|
||||
main-is: Spec.hs
|
||||
other-modules:
|
||||
Paths_lab3
|
||||
autogen-modules:
|
||||
Paths_lab3
|
||||
hs-source-dirs:
|
||||
test
|
||||
ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N
|
||||
build-depends:
|
||||
JuicyPixels
|
||||
, base >=4.7 && <5
|
||||
, lab3
|
||||
, vector
|
||||
default-language: Haskell2010
|
||||
61
lab3/package.yaml
Normal file
@@ -0,0 +1,61 @@
|
||||
name: lab3
|
||||
version: 0.1.0.0
|
||||
github: "githubuser/lab3"
|
||||
license: BSD-3-Clause
|
||||
author: "Author name here"
|
||||
maintainer: "example@example.com"
|
||||
copyright: "2024 Author name here"
|
||||
|
||||
extra-source-files:
|
||||
- README.md
|
||||
- CHANGELOG.md
|
||||
|
||||
# Metadata used when publishing your package
|
||||
# synopsis: Short description of your package
|
||||
# category: Web
|
||||
|
||||
# To avoid duplicated efforts in documentation and dealing with the
|
||||
# complications of embedding Haddock markup inside cabal files, it is
|
||||
# common to point users to the README.md file.
|
||||
description: Please see the README on GitHub at <https://github.com/githubuser/lab3#readme>
|
||||
|
||||
dependencies:
|
||||
- base >= 4.7 && < 5
|
||||
- JuicyPixels
|
||||
- vector
|
||||
|
||||
ghc-options:
|
||||
- -Wall
|
||||
- -Wcompat
|
||||
- -Widentities
|
||||
- -Wincomplete-record-updates
|
||||
- -Wincomplete-uni-patterns
|
||||
- -Wmissing-export-lists
|
||||
- -Wmissing-home-modules
|
||||
- -Wpartial-fields
|
||||
- -Wredundant-constraints
|
||||
|
||||
library:
|
||||
source-dirs: src
|
||||
|
||||
executables:
|
||||
lab3-exe:
|
||||
main: Main.hs
|
||||
source-dirs: app
|
||||
ghc-options:
|
||||
- -threaded
|
||||
- -rtsopts
|
||||
- -with-rtsopts=-N
|
||||
dependencies:
|
||||
- lab3
|
||||
|
||||
tests:
|
||||
lab3-test:
|
||||
main: Spec.hs
|
||||
source-dirs: test
|
||||
ghc-options:
|
||||
- -threaded
|
||||
- -rtsopts
|
||||
- -with-rtsopts=-N
|
||||
dependencies:
|
||||
- lab3
|
||||
5
lab3/report/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
**/*
|
||||
!.gitignore
|
||||
!report.tex
|
||||
!img
|
||||
!img/*
|
||||
BIN
lab3/report/img/david.jpg
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
lab3/report/img/david_1_20.jpg
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
lab3/report/img/david_2_20.jpg
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
lab3/report/img/david_3_20.jpg
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
lab3/report/img/david_4_20.jpg
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
lab3/report/img/david_5_20.jpg
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
lab3/report/img/david_6_20.jpg
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
lab3/report/img/david_7_20.jpg
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
lab3/report/img/david_8_20.jpg
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
lab3/report/img/results.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
463
lab3/report/report.tex
Normal file
@@ -0,0 +1,463 @@
|
||||
\documentclass[a4paper, final]{article}
|
||||
%\usepackage{literat} % Нормальные шрифты
|
||||
\usepackage[14pt]{extsizes} % для того чтобы задать нестандартный 14-ый размер шрифта
|
||||
\usepackage{tabularx}
|
||||
\usepackage[T2A]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[russian]{babel}
|
||||
\usepackage{amsmath}
|
||||
\usepackage[left=25mm, top=20mm, right=20mm, bottom=20mm, footskip=10mm]{geometry}
|
||||
\usepackage{ragged2e} %для растягивания по ширине
|
||||
\usepackage{setspace} %для межстрочного интервала
|
||||
\usepackage{moreverb} %для работы с листингами
|
||||
\usepackage{indentfirst} % для абзацного отступа
|
||||
\usepackage{moreverb} %для печати в листинге исходного кода программ
|
||||
\usepackage{pdfpages} %для вставки других pdf файлов
|
||||
\usepackage{tikz}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{afterpage}
|
||||
\usepackage{longtable}
|
||||
\usepackage{float}
|
||||
|
||||
|
||||
|
||||
% \usepackage[paper=A4,DIV=12]{typearea}
|
||||
\usepackage{pdflscape}
|
||||
% \usepackage{lscape}
|
||||
|
||||
\usepackage{array}
|
||||
\usepackage{multirow}
|
||||
|
||||
\renewcommand\verbatimtabsize{4\relax}
|
||||
\renewcommand\listingoffset{0.2em} %отступ от номеров строк в листинге
|
||||
\renewcommand{\arraystretch}{1.4} % изменяю высоту строки в таблице
|
||||
\usepackage[font=small, singlelinecheck=false, justification=centering, format=plain, labelsep=period]{caption} %для настройки заголовка таблицы
|
||||
\usepackage{listings} %листинги
|
||||
\usepackage{xcolor} % цвета
|
||||
\usepackage{hyperref}% для гиперссылок
|
||||
\usepackage{enumitem} %для перечислений
|
||||
|
||||
% Настраиваем листинги, чтобы они использовали счётчик figure
|
||||
% \AtBeginDocument{
|
||||
% \renewcommand{\thelstlisting}{\thefigure} % Листинги используют тот же счетчик, что и рисунки
|
||||
% \renewcommand{\lstlistingname}{Рис.} % Меняем подпись на "Рисунок"
|
||||
% }
|
||||
|
||||
% Автоматически увеличиваем счетчик figure перед каждым листингом
|
||||
% \let\oldlstlisting\lstlisting
|
||||
% \renewcommand{\lstlisting}[1][]{%
|
||||
% \refstepcounter{figure}% Увеличиваем счетчик figure
|
||||
% \oldlstlisting[#1]% Вызываем оригинальную команду lstlisting
|
||||
% }
|
||||
|
||||
\newcommand{\specialcell}[2][l]{\begin{tabular}[#1]{@{}l@{}}#2\end{tabular}}
|
||||
|
||||
|
||||
\setlist[enumerate,itemize]{leftmargin=1.2cm} %отступ в перечислениях
|
||||
|
||||
\hypersetup{colorlinks,
|
||||
allcolors=[RGB]{010 090 200}} %красивые гиперссылки (не красные)
|
||||
|
||||
% подгружаемые языки — подробнее в документации listings (это всё для листингов)
|
||||
\lstloadlanguages{ Haskell}
|
||||
% включаем кириллицу и добавляем кое−какие опции
|
||||
\lstset{tabsize=2,
|
||||
breaklines,
|
||||
basicstyle=\footnotesize,
|
||||
columns=fullflexible,
|
||||
flexiblecolumns,
|
||||
numbers=left,
|
||||
numberstyle={\footnotesize},
|
||||
keywordstyle=\color{blue},
|
||||
inputencoding=cp1251,
|
||||
extendedchars=true
|
||||
}
|
||||
\lstdefinelanguage{MyC}{
|
||||
language=Haskell,
|
||||
% ndkeywordstyle=\color{darkgray}\bfseries,
|
||||
% identifierstyle=\color{black},
|
||||
% morecomment=[n]{/**}{*/},
|
||||
% commentstyle=\color{blue}\ttfamily,
|
||||
% stringstyle=\color{red}\ttfamily,
|
||||
% morestring=[b]",
|
||||
% showstringspaces=false,
|
||||
% morecomment=[l][\color{gray}]{//},
|
||||
keepspaces=true,
|
||||
escapechar=\%,
|
||||
texcl=true
|
||||
}
|
||||
|
||||
\textheight=24cm % высота текста
|
||||
\textwidth=16cm % ширина текста
|
||||
\oddsidemargin=0pt % отступ от левого края
|
||||
\topmargin=-1.5cm % отступ от верхнего края
|
||||
\parindent=24pt % абзацный отступ
|
||||
\parskip=5pt % интервал между абзацами
|
||||
\tolerance=2000 % терпимость к "жидким" строкам
|
||||
\flushbottom % выравнивание высоты страниц
|
||||
|
||||
|
||||
% Настройка листингов
|
||||
\lstset{
|
||||
language=Haskell,
|
||||
extendedchars=\true,
|
||||
inputencoding=utf8,
|
||||
keepspaces=true,
|
||||
captionpos=t,
|
||||
}
|
||||
|
||||
\begin{document} % начало документа
|
||||
|
||||
|
||||
|
||||
% НАЧАЛО ТИТУЛЬНОГО ЛИСТА
|
||||
\begin{center}
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ\\
|
||||
федеральное государственное автономное образовательное учреждение высшего образования «Санкт-Петербургский политехнический университет Петра Великого»\\[10pt]}
|
||||
\normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt]
|
||||
\normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt]
|
||||
\normalsize{Направление: 02.03.01 <<Математика и компьютерные науки>>}\\
|
||||
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\large{Отчет по лабораторной работе №3}\\
|
||||
\large{по дисциплине}\\
|
||||
\large{<<Функциональное программирование>>}\\
|
||||
\large{Вариант 20}\\
|
||||
\hfill \break
|
||||
|
||||
% \hfill \break
|
||||
% \hfill \break
|
||||
\end{center}
|
||||
|
||||
\small{
|
||||
\begin{tabular}{lrrl}
|
||||
\!\!\!Студент, & \hspace{2cm} & & \\
|
||||
\!\!\!группы 5130201/20102 & \hspace{2cm} & \underline{\hspace{3cm}} &Тищенко А. А. \\\\
|
||||
\!\!\!Преподаватель,\\ \hspace{-5pt}к. т. н., доц. & \hspace{2cm} & \underline{\hspace{3cm}} & Моторин Д. Е. \\\\
|
||||
&&\hspace{4cm}
|
||||
\end{tabular}
|
||||
\begin{flushright}
|
||||
<<\underline{\hspace{1cm}}>>\underline{\hspace{2.5cm}} 2024г.
|
||||
\end{flushright}
|
||||
}
|
||||
|
||||
\hfill \break
|
||||
% \hfill \break
|
||||
\begin{center} \small{Санкт-Петербург, 2024} \end{center}
|
||||
\thispagestyle{empty} % выключаем отображение номера для этой страницы
|
||||
|
||||
% КОНЕЦ ТИТУЛЬНОГО ЛИСТА
|
||||
\newpage
|
||||
|
||||
\tableofcontents
|
||||
|
||||
|
||||
% \newpage
|
||||
|
||||
% \section*{Введение}
|
||||
|
||||
% \addcontentsline{toc}{section}{Введение}
|
||||
|
||||
\newpage
|
||||
\section {Постановка задачи}
|
||||
Для выполнения лабораторной работы необходимо было сделать следующее. Создать проект в stack. Все чистые функции записать в библиотеку Lib.hs и ограничить доступ к вспомогательным функциям. Использовать do-нотацию для работы с внешними файлами. Найти портрет Дэвида Дойча. Перевести изображение в формат .bmp (24-разрядный). Сохранить в файл формата .txt фрагмент биографии (не менее 1000 символов без пробелов, текст не должен обрываться на середине слова или предложения). Закодировать текст в изображение шифром Цезаря (смещение задается пользователем). Ключ к шифру записывается в имя файла. Написать функцию расшифровывающую текст из изображения используя ключ из имени файла и сохраняющую результат в отдельный текстовый файл. Создать функции шифрующие текст в последний бит каждого байта, последние два бита
|
||||
каждого байта, …, все биты в байте. В отчете привести примеры искажений изображения.
|
||||
|
||||
|
||||
\newpage
|
||||
\section {Математическое описание}
|
||||
\subsection{Шифр Цезаря}
|
||||
Шифр Цезаря (лат. Notae Caesarianae), также известный как шифр сдвига или код Цезаря — разновидность шифра подстановки, в котором каждый символ в открытом тексте заменяется символом, находящимся на некотором постоянном числе позиций левее или правее него в алфавите (так, в шифре со сдвигом вправо на 3, А была бы заменена на Г, Б станет Д, и так далее). Шифр был назван в честь римского полководца Гая Юлия Цезаря, использовавшего его для секретной переписки со своими военачальниками.
|
||||
|
||||
Если сопоставить каждому символу алфавита его порядковый номер (нумеруя с 0), то шифрование и дешифрование можно выразить формулами модульной арифметики~\cite{caesar}:
|
||||
\[
|
||||
y = (x + k) \mod n
|
||||
\]
|
||||
\[
|
||||
x = (y - k) \mod n
|
||||
\]
|
||||
где: \\
|
||||
$x$ — символ открытого текста, \\
|
||||
$y$ — символ шифрованного текста, \\
|
||||
$n$ — мощность алфавита, \\
|
||||
$k$ — ключ.
|
||||
|
||||
|
||||
\newpage
|
||||
\section {Особенности реализации}
|
||||
\subsection{Исходное изображение и текст}
|
||||
Для выполнения лабораторной работы необходимо было найти изображение Дэвида Дойча (см. Рис.~\ref{fig:david}). Изображение было переведено из формата \texttt{jpeg} в формат \texttt{bmp} с помощью сайта~\cite{convertio}.
|
||||
|
||||
\begin{figure}[h]
|
||||
\centering
|
||||
\includegraphics[width=0.5\linewidth]{img/david.jpg}
|
||||
\caption{Изображение Дэвида Дойча, размещённое на его личном сайте~\cite{david}.}
|
||||
\label{fig:david}
|
||||
\end{figure}
|
||||
|
||||
Отрывок биографии Дэвида Дойча длиною в 1157 символов без учёта пробелов представлен ниже.
|
||||
|
||||
\texttt{
|
||||
David Elieser Deutsch FRS (DOYTCH; born 18 May 1953) is a British physicist at the University of Oxford. He is a visiting professor in the Department of Atomic and Laser Physics at the Centre for Quantum Computation (CQC) in the Clarendon Laboratory of the University of Oxford. He pioneered the field of quantum computation by formulating a description for a quantum Turing machine, as well as specifying an algorithm designed to run on a quantum computer. He is a proponent of the many-worlds interpretation of quantum mechanics.
|
||||
Deutsch was born to a Jewish family in Haifa, Israel on 18 May 1953, the son of Oskar and Tikva Deutsch. In London, David attended Geneva House school in Cricklewood (his parents owned and ran the Alma restaurant on Cricklewood Broadway), followed by William Ellis School in Highgate before reading Natural Sciences at Clare College, Cambridge and taking Part III of the Mathematical Tripos. He went on to Wolfson College, Oxford for his doctorate in theoretical physics, about quantum field theory in curved space-time, supervised by Dennis Sciama and Philip Candelas.
|
||||
His work on quantum algorithms began with a 1985 paper, later expanded in 1992 along with Richard Jozsa, to produce the DeutschJozsa algorithm, one of the first examples of a quantum algorithm that is exponentially faster than any possible deterministic classical algorithm.
|
||||
}
|
||||
|
||||
\subsection{Кодирование и декодирование текста с помощью шифра Цезаря}
|
||||
|
||||
Код функций для кодирования и декодирования текста с помощью шифра Цезаря представлен в листинге~\ref{lst:encrypt-caesar}. Функция \texttt{encryptCaesar} принимает алфавит в виде списка символов, смещение и сам текст, а возвращает зашифрованный текст. В её коде используется вспомогательная функция \texttt{indexOf}. Функция принимает список и элемент списка, а возвращает индекс этого элемента. Для создания алфавита используется функция \texttt{createAlphabetFromText}. Она принимает текст, а возвращает алфавит, который в нём используется, в виде списка символов. Для декодирования текста используется функция \texttt{decryptCaesar}, которая, по-сути, является лишь обёрткой над функцией \texttt{encryptCaesar}, так как процесс кодирования осуществляется почти так же как и декодирования. Функция \texttt{decryptCaesar} принимает на вход алфавит, смещение и закодированный текст, а возвращает декодированный текст. Алфавит сохраняется в отдельный файл и должен передаваться вместе с зашифрованным текстом, чтобы этот текст можно было дешифровать.
|
||||
|
||||
\begin{lstlisting}[caption={Функции для кодирования и декодирования текста с помощью шифра Цезаря.}, label={lst:encrypt-caesar}]
|
||||
encryptCaesar :: [Char] -> Int -> String -> String
|
||||
encryptCaesar alphabet shift text = map caesarChar text
|
||||
where
|
||||
caesarChar c = alphabet !! ((indexOf alphabet c + shift) `mod` length alphabet)
|
||||
|
||||
indexOf :: (Eq t) => [t] -> t -> Int
|
||||
indexOf [] _ = -1
|
||||
indexOf (x : xs) target
|
||||
| x == target = 0
|
||||
| otherwise = 1 + indexOf xs target
|
||||
|
||||
createAlphabetFromText :: String -> [Char]
|
||||
createAlphabetFromText [] = []
|
||||
createAlphabetFromText (x:xs)
|
||||
| x `elem` alphabet = alphabet
|
||||
| otherwise = x : alphabet
|
||||
where
|
||||
alphabet = createAlphabetFromText xs
|
||||
|
||||
decryptCaesar :: [Char] -> Int -> String -> String
|
||||
decryptCaesar alphabet shift =
|
||||
encryptCaesar alphabet (alphabetLength - (shift `mod` alphabetLength))
|
||||
where
|
||||
alphabetLength = length alphabet
|
||||
\end{lstlisting}
|
||||
|
||||
Пример закодированного с помощью шифра Цезаря текста биографии Дэвида Дойча для смещения 5 представлен ниже.
|
||||
|
||||
\noindent
|
||||
\texttt{
|
||||
uiHF rWtFaga.ruabYgo;r3qkrNuCLS59Grcm.lrDwrvinrDJRBTrFgrirM.FYFg;rs;ngFo\\
|
||||
FgYriYrY;arAlFHa.gFYnrmerCdem. Ur9arFgrirHFgFYFlhrs.meaggm.rFlrY;aruasi.\\
|
||||
YQalYrmerIYmQForil r)iga.r8;ngFogriYrY;ar5alY.arem.r(bilYbQr5mQsbYiYFmlr\\
|
||||
N5(5TrFlrY;ar5ti.al mlr)icm.iYm.nrmerY;arAlFHa.gFYnrmerCdem. Ur9arsFmlaa\\
|
||||
.a rY;areFat rmerpbilYbQromQsbYiYFmlrcnrem.QbtiYFlhrir ago.FsYFmlrem.rir\\
|
||||
pbilYbQrSb.FlhrQio;Flayrigr,attrigrgsaoFenFlhrilrithm.FY;Qr agFhla rYmr.\\
|
||||
blrmlrirpbilYbQromQsbYa.Ur9arFgrirs.msmlalYrmerY;arQiln,m.t grFlYa.s.aYi\\
|
||||
YFmlrmerpbilYbQrQao;ilFogU1uabYgo;r,igrcm.lrYmrirxa,Fg;reiQFtnrFlr9iFeiy\\
|
||||
r-g.iatrmlrDwrvinrDJRByrY;argmlrmerCg2i.ril rSF2HiruabYgo;Ur-lr)ml mlyru\\
|
||||
iHF riYYal a rEalaHir9mbgargo;mmtrFlr5.Fo2ta,mm rN;Fgrsi.alYgrm,la ril r\\
|
||||
.ilrY;arItQir.agYib.ilYrmlr5.Fo2ta,mm rM.mi ,inTyremttm,a rcnrPFttFiQrWt\\
|
||||
tFgrko;mmtrFlr9Fh;hiYarcaem.ar.ai FlhrOiYb.itrkoFaloagriYr5ti.ar5mttahay\\
|
||||
r5iQc.F haril rYi2Flhr8i.Yr---rmerY;arviY;aQiYFoitrS.FsmgUr9ar,alYrmlrYm\\
|
||||
rPmtegmlr5mttahayrCdem. rem.r;Fgr moYm.iYarFlrY;am.aYFoitrs;ngFogyricmbY\\
|
||||
rpbilYbQreFat rY;am.nrFlrob.Ha rgsioaYFQayrgbsa.HFga rcnruallFgrkoFiQiri\\
|
||||
l r8;FtFsr5il atigU19Fgr,m.2rmlrpbilYbQrithm.FY;Qgrcahilr,FY;rirDJwRrsis\\
|
||||
a.yrtiYa.radsil a rFlrDJJzritmlhr,FY;rqFo;i. rxmfgiyrYmrs.m boarY;aruabY\\
|
||||
go;xmfgirithm.FY;QyrmlarmerY;areF.gYradiQstagrmerirpbilYbQrithm.FY;QrY;i\\
|
||||
YrFgradsmlalYFittnreigYa.rY;ilrilnrsmggFctar aYa.QFlFgYForotiggFoitrithm\\
|
||||
.FY;QU}
|
||||
|
||||
|
||||
\subsection{Представление текста в виде последовательности бит}
|
||||
|
||||
Код функций для преобразования текста в последовательность бит и обратно представлен в листинге~\ref{lst:bit}. Функция \texttt{textToBits} принимает текст в виде строки и возвращает его представление в виде вектора бит. Она использует вспомогательную функцию \texttt{charToBits}, которая преобразует символ в список бит, представляющих его код ASCII в двоичном виде. Для преобразования последовательности бит обратно в текст используется функция \texttt{bitsToText}. Она рекурсивно делит вектор бит на блоки по 8 бит, преобразует каждый блок в символ ASCII и объединяет их в строку. В процессе этого преобразования используется функция \texttt{bitsToInt}, которая преобразует вектор бит в целое число, интерпретируя их как двоичное представление этого числа.
|
||||
|
||||
|
||||
\begin{lstlisting}[caption={Функции для конвертации текста в последовательность бит и обратно.}, label={lst:bit}]
|
||||
textToBits :: String -> VU.Vector Int
|
||||
textToBits text = VU.fromList $ concatMap charToBits text
|
||||
|
||||
charToBits :: Char -> [Int]
|
||||
charToBits c = [if testBit (ord c) i then 1 else 0 | i <- [7,6..0]]
|
||||
|
||||
bitsToText :: VU.Vector Int -> String
|
||||
bitsToText bits
|
||||
| VU.null bits = []
|
||||
| otherwise = (chr $ bitsToInt (VU.take 8 bits)) : bitsToText (VU.drop 8 bits)
|
||||
|
||||
bitsToInt :: VU.Vector Int -> Int
|
||||
bitsToInt bits =
|
||||
sum [bit * (2 ^ index) | (bit, index) <- zip (VU.toList bits) [len,(len - 1)..0]]
|
||||
where
|
||||
len = VU.length bits - 1
|
||||
\end{lstlisting}
|
||||
|
||||
|
||||
\subsection{Работа с файлами}
|
||||
Для работы с текстовыми файлами использовались базовые функции Haskell -- \texttt{readFile} (читает содержимое файла и возвращает его как строку) и \texttt{writeFile} (записывает строку в файл, заменяя его содержимое).
|
||||
|
||||
Для работы с изображениями использовалась библиотека \texttt{JuicyPixels}~\cite{JuicyPixels}. С её помощью можно как прочитать изображение в любом популярном формате, так и сохранить его. В частности в работе использовались функции: \texttt{readImage} -- для чтения изображения из указанного файла, \texttt{saveBmpImage} -- для сохранения изображения в формате bmp.
|
||||
|
||||
|
||||
\subsection{Сохранение зашифрованных данных в изображении}
|
||||
|
||||
Код функций для создания изображения с закодированными данными представлен в листинге~\ref{lst:genImg}. Функция \texttt{encodePixel} отвечает за кодирование последовательности бит в определённый пиксель изображения. Она принимает количество бит данных, которое будет сохранено в каждый байт изображения, исходное изображение, вектор бит зашифрованных данных, координаты пикселя (\(x, y\)) и возвращает новый пиксель с закодированными данными. Для этого функция вычисляет индекс пикселя в изображении, извлекает соответствующую часть вектора бит данных, преобразует её в целые числа, накладывает битовую маску, которая соответствует количество изменяемых бит в байте, и записывает закодированные данные. Для создания маски используется вспомогательная функция \texttt{createMask}.
|
||||
|
||||
Функция \texttt{encodePixel} затем используется вместе с функцией \texttt{generateImage} из библиотеки JuicyPixels для генерации нового изображения.
|
||||
|
||||
При сохранении изображения в файл, в его названии сохраняется смещение шифра Цезаря и количество бит в байте, отведённых для хранения зашифрованных данных. Например, название изображения \texttt{david\_2\_10.bmp} означает, что при кодировании использовался код Цезаря со смещением 10, а для хранения закодированных данных в каждом байте изображения использовалось 2 бита.
|
||||
|
||||
\begin{lstlisting}[caption={Функции для создания изображения с закодированными данными.}, label={lst:genImg}]
|
||||
createMask :: Int -> Int
|
||||
createMask shift = shiftL (complement 0) shift .&. complement 0
|
||||
|
||||
encodePixel :: Int -> Image PixelRGB8 -> VU.Vector Int -> Int -> Int -> PixelRGB8
|
||||
encodePixel bitsPerByte img bits x y = PixelRGB8 newR newG newB
|
||||
where
|
||||
width = imageWidth img
|
||||
|
||||
index = x + y * width
|
||||
startPos = index * 3 * bitsPerByte
|
||||
pixelBits = VU.slice startPos (3 * bitsPerByte) bits
|
||||
|
||||
bitsIntR = bitsToInt $ VU.slice 0 bitsPerByte pixelBits
|
||||
bitsIntG = bitsToInt $ VU.slice bitsPerByte bitsPerByte pixelBits
|
||||
bitsIntB = bitsToInt $ VU.slice (2 * bitsPerByte) bitsPerByte pixelBits
|
||||
|
||||
mask = createMask bitsPerByte
|
||||
|
||||
PixelRGB8 r g b = pixelAt img x y
|
||||
newR = intToWord8 $ ((word8ToInt r) .&. mask) .|. bitsIntR
|
||||
newG = intToWord8 $ ((word8ToInt g) .&. mask) .|. bitsIntG
|
||||
newB = intToWord8 $ ((word8ToInt b) .&. mask) .|. bitsIntB
|
||||
\end{lstlisting}
|
||||
|
||||
|
||||
\subsection{Чтение зашифрованных данных из изображения}
|
||||
|
||||
Код функций для чтения зашифрованных данных из изображения представлен в листинге~\ref{lst:readImg}. Функция \texttt{extractBits} извлекает заданное количество бит из одного байта пикселя. Она принимает число бит на байт и байт пикселя, возвращая список бит. Функция \texttt{extractBitsFromPixel} предназначена для извлечения бит из всех трёх цветовых каналов (\(R, G, B\)) пикселя. Она объединяет списки бит из каждого канала в один общий список. Для извлечения бит из всего изображения используется функция \texttt{extractBitsFromImage}. Она последовательно обрабатывает все пиксели изображения, извлекая биты с помощью \texttt{extractBitsFromPixel}, и объединяет их в общий список.
|
||||
|
||||
Функция \texttt{extractShift} извлекает смещения для шифра Цезаря из названия файла изображения.
|
||||
|
||||
\begin{lstlisting}[caption={Функции для чтения зашифрованных данных из изображения.}, label={lst:readImg}]
|
||||
extractBits :: Int -> Pixel8 -> [Int]
|
||||
extractBits bitsPerByte pixelByte =
|
||||
[ if testBit pixelByte i then 1 else 0 | i <- [bitsPerByte-1, bitsPerByte-2..0] ]
|
||||
|
||||
extractBitsFromPixel :: Int -> PixelRGB8 -> [Int]
|
||||
extractBitsFromPixel bitsPerByte (PixelRGB8 r g b) =
|
||||
let bitsR = extractBits bitsPerByte r
|
||||
bitsG = extractBits bitsPerByte g
|
||||
bitsB = extractBits bitsPerByte b
|
||||
in bitsR ++ bitsG ++ bitsB
|
||||
|
||||
extractBitsFromImage :: Int -> Image PixelRGB8 -> [Int]
|
||||
extractBitsFromImage bitsPerByte img =
|
||||
let width = imageWidth img
|
||||
height = imageHeight img
|
||||
pixels = [ pixelAt img x y | y <- [0..height - 1], x <- [0..width - 1] ]
|
||||
in concatMap (extractBitsFromPixel bitsPerByte) pixels
|
||||
|
||||
extractShift :: String -> Maybe Int
|
||||
extractShift path =
|
||||
let shift = takeWhile (`elem` ['0'..'9']) (reverse $ takeWhile (/= '_') (reverse path))
|
||||
in readMaybe shift
|
||||
\end{lstlisting}
|
||||
|
||||
|
||||
\newpage
|
||||
\section {Результаты работы программы}
|
||||
При успешном завершении программа создаёт четыре файла: файл изображения с закодированных текстом, текстовый файл с закодированным текстом, текстовый файл с алфавитом и текстовый файл с декодированным текстом.
|
||||
|
||||
\begin{figure}[h]
|
||||
\centering
|
||||
\includegraphics[width=0.65\linewidth]{img/results.png}
|
||||
\caption{Результаты работы программы в консоли.}
|
||||
\label{fig:results}
|
||||
\end{figure}
|
||||
|
||||
На Рис.~\ref{fig:results} представлены результаты работы программы в консоли.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.33\linewidth]{img/david_1_20.jpg}
|
||||
\caption{Изображение с зашифрованными данными (1 бит).}
|
||||
\label{fig:david1}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.33\linewidth]{img/david_2_20.jpg}
|
||||
\caption{Изображение с зашифрованными данными (2 бит).}
|
||||
\label{fig:david2}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.33\linewidth]{img/david_3_20.jpg}
|
||||
\caption{Изображение с зашифрованными данными (3 бит).}
|
||||
\label{fig:david3}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.33\linewidth]{img/david_4_20.jpg}
|
||||
\caption{Изображение с зашифрованными данными (4 бит).}
|
||||
\label{fig:david4}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.33\linewidth]{img/david_5_20.jpg}
|
||||
\caption{Изображение с зашифрованными данными (5 бит).}
|
||||
\label{fig:david5}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.33\linewidth]{img/david_6_20.jpg}
|
||||
\caption{Изображение с зашифрованными данными (6 бит).}
|
||||
\label{fig:david6}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.33\linewidth]{img/david_7_20.jpg}
|
||||
\caption{Изображение с зашифрованными данными (7 бит).}
|
||||
\label{fig:david7}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.33\linewidth]{img/david_8_20.jpg}
|
||||
\caption{Изображение с зашифрованными данными (8 бит).}
|
||||
\label{fig:david8}
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
\phantom{text}
|
||||
\newpage
|
||||
\phantom{text}
|
||||
\newpage
|
||||
На Рис.~\ref{fig:david1}-\ref{fig:david8} представлены результирующие изображения с разным количеством бит, отведённых под зашифрованные данные.
|
||||
|
||||
|
||||
\newpage
|
||||
\section*{Заключение}
|
||||
\addcontentsline{toc}{section}{Заключение}
|
||||
В результате выполнения лабораторной работы была создана программа на языке Haskell, которая способна кодировать текстовые данных из указанного файла с помощью шифра Цезаря и сохранять эти данные внутрь изображения. Причём программа позволяет выбрать как смещение для шифра Цезаря, так и количество бит, которое будет использовано в каждом байте изображения для хранения данных.
|
||||
|
||||
|
||||
\newpage
|
||||
\section*{Список литературы}
|
||||
\addcontentsline{toc}{section}{Список литературы}
|
||||
|
||||
\vspace{-1.5cm}
|
||||
\begin{thebibliography}{0}
|
||||
\bibitem{caesar}
|
||||
Luciano, D., Prichett, G., Cryptology: From Caesar Ciphers to Public-Key Cryptosystems, The College Mathematics Journal, 1987.
|
||||
\bibitem{david}
|
||||
David Deutsch -- personal website, URL: \url{https://www.daviddeutsch.org.uk/}, Дата обращения: 19.11.2024
|
||||
\bibitem{convertio}
|
||||
Convertio -- BPM to JPG online converter, URL: \url{https://convertio.co/ru/bmp-jpg/}, Дата обращения: 19.11.2024
|
||||
\bibitem{JuicyPixels}
|
||||
Hackage -- JuicyPixels: Picture loading/serialization, URL: \url{https://hackage.haskell.org/package/JuicyPixels}, Дата обращения: 19.11.2024
|
||||
\end{thebibliography}
|
||||
\end{document}
|
||||
3
lab3/resources/biography.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
David Elieser Deutsch FRS (DOYTCH; born 18 May 1953) is a British physicist at the University of Oxford. He is a visiting professor in the Department of Atomic and Laser Physics at the Centre for Quantum Computation (CQC) in the Clarendon Laboratory of the University of Oxford. He pioneered the field of quantum computation by formulating a description for a quantum Turing machine, as well as specifying an algorithm designed to run on a quantum computer. He is a proponent of the many-worlds interpretation of quantum mechanics.
|
||||
Deutsch was born to a Jewish family in Haifa, Israel on 18 May 1953, the son of Oskar and Tikva Deutsch. In London, David attended Geneva House school in Cricklewood (his parents owned and ran the Alma restaurant on Cricklewood Broadway), followed by William Ellis School in Highgate before reading Natural Sciences at Clare College, Cambridge and taking Part III of the Mathematical Tripos. He went on to Wolfson College, Oxford for his doctorate in theoretical physics, about quantum field theory in curved space-time, supervised by Dennis Sciama and Philip Candelas.
|
||||
His work on quantum algorithms began with a 1985 paper, later expanded in 1992 along with Richard Jozsa, to produce the DeutschJozsa algorithm, one of the first examples of a quantum algorithm that is exponentially faster than any possible deterministic classical algorithm.
|
||||
BIN
lab3/resources/david.bmp
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
lab3/resources/david.jpg
Normal file
|
After Width: | Height: | Size: 98 KiB |
117
lab3/src/Lib.hs
Normal file
@@ -0,0 +1,117 @@
|
||||
module Lib
|
||||
(
|
||||
createAlphabetFromText,
|
||||
encryptCaesar,
|
||||
decryptCaesar,
|
||||
textToBits,
|
||||
bitsToText,
|
||||
encodePixel,
|
||||
extractShift,
|
||||
extractBitsFromImage
|
||||
)
|
||||
where
|
||||
|
||||
import Codec.Picture
|
||||
|
||||
import Text.Read (readMaybe)
|
||||
import Data.Word (Word8)
|
||||
import Data.Char (ord, chr)
|
||||
import Data.Bits (testBit, shiftL, complement, (.|.), (.&.))
|
||||
import qualified Data.Vector.Unboxed as VU
|
||||
|
||||
createAlphabetFromText :: String -> [Char]
|
||||
createAlphabetFromText [] = []
|
||||
createAlphabetFromText (x:xs)
|
||||
| x `elem` alphabet = alphabet
|
||||
| otherwise = x : alphabet
|
||||
where
|
||||
alphabet = createAlphabetFromText xs
|
||||
|
||||
indexOf :: (Eq t) => [t] -> t -> Int
|
||||
indexOf [] _ = -1
|
||||
indexOf (x : xs) target
|
||||
| x == target = 0
|
||||
| otherwise = 1 + indexOf xs target
|
||||
|
||||
encryptCaesar :: [Char] -> Int -> String -> String
|
||||
encryptCaesar alphabet shift text = map caesarChar text
|
||||
where
|
||||
caesarChar c = alphabet !! ((indexOf alphabet c + shift) `mod` length alphabet)
|
||||
|
||||
decryptCaesar :: [Char] -> Int -> String -> String
|
||||
decryptCaesar alphabet shift =
|
||||
encryptCaesar alphabet (alphabetLength - (shift `mod` alphabetLength))
|
||||
where
|
||||
alphabetLength = length alphabet
|
||||
|
||||
textToBits :: String -> VU.Vector Int
|
||||
textToBits text = VU.fromList $ concatMap charToBits text
|
||||
|
||||
charToBits :: Char -> [Int]
|
||||
charToBits c = [if testBit (ord c) i then 1 else 0 | i <- [7,6..0]]
|
||||
|
||||
-- intToBits :: Int -> [Int]
|
||||
-- intToBits n = [if testBit n i then 1 else 0 | i <- [31,30..0]]
|
||||
|
||||
bitsToText :: VU.Vector Int -> String
|
||||
bitsToText bits
|
||||
| VU.null bits = []
|
||||
| otherwise = (chr $ bitsToInt (VU.take 8 bits)) : bitsToText (VU.drop 8 bits)
|
||||
|
||||
bitsToInt :: VU.Vector Int -> Int
|
||||
bitsToInt bits =
|
||||
sum [bit * (2 ^ index) | (bit, index) <- zip (VU.toList bits) [len,(len - 1)..0]]
|
||||
where
|
||||
len = VU.length bits - 1
|
||||
|
||||
intToWord8 :: Int -> Word8
|
||||
intToWord8 x = fromIntegral x
|
||||
|
||||
word8ToInt :: Word8 -> Int
|
||||
word8ToInt x = fromIntegral x
|
||||
|
||||
createMask :: Int -> Int
|
||||
createMask shift = shiftL (complement 0) shift .&. complement 0
|
||||
|
||||
encodePixel :: Int -> Image PixelRGB8 -> VU.Vector Int -> Int -> Int -> PixelRGB8
|
||||
encodePixel bitsPerByte img bits x y = PixelRGB8 newR newG newB
|
||||
where
|
||||
width = imageWidth img
|
||||
|
||||
index = x + y * width
|
||||
startPos = index * 3 * bitsPerByte
|
||||
pixelBits = VU.slice startPos (3 * bitsPerByte) bits
|
||||
|
||||
bitsIntR = bitsToInt $ VU.slice 0 bitsPerByte pixelBits
|
||||
bitsIntG = bitsToInt $ VU.slice bitsPerByte bitsPerByte pixelBits
|
||||
bitsIntB = bitsToInt $ VU.slice (2 * bitsPerByte) bitsPerByte pixelBits
|
||||
|
||||
mask = createMask bitsPerByte
|
||||
|
||||
PixelRGB8 r g b = pixelAt img x y
|
||||
newR = intToWord8 $ ((word8ToInt r) .&. mask) .|. bitsIntR
|
||||
newG = intToWord8 $ ((word8ToInt g) .&. mask) .|. bitsIntG
|
||||
newB = intToWord8 $ ((word8ToInt b) .&. mask) .|. bitsIntB
|
||||
|
||||
extractBits :: Int -> Pixel8 -> [Int]
|
||||
extractBits bitsPerByte pixelByte =
|
||||
[ if testBit pixelByte i then 1 else 0 | i <- [bitsPerByte-1, bitsPerByte-2..0] ]
|
||||
|
||||
extractBitsFromPixel :: Int -> PixelRGB8 -> [Int]
|
||||
extractBitsFromPixel bitsPerByte (PixelRGB8 r g b) =
|
||||
let bitsR = extractBits bitsPerByte r
|
||||
bitsG = extractBits bitsPerByte g
|
||||
bitsB = extractBits bitsPerByte b
|
||||
in bitsR ++ bitsG ++ bitsB
|
||||
|
||||
extractBitsFromImage :: Int -> Image PixelRGB8 -> [Int]
|
||||
extractBitsFromImage bitsPerByte img =
|
||||
let width = imageWidth img
|
||||
height = imageHeight img
|
||||
pixels = [ pixelAt img x y | y <- [0..height - 1], x <- [0..width - 1] ]
|
||||
in concatMap (extractBitsFromPixel bitsPerByte) pixels
|
||||
|
||||
extractShift :: String -> Maybe Int
|
||||
extractShift path =
|
||||
let shift = takeWhile (`elem` ['0'..'9']) (reverse $ takeWhile (/= '_') (reverse path))
|
||||
in readMaybe shift
|
||||
67
lab3/stack.yaml
Normal file
@@ -0,0 +1,67 @@
|
||||
# This file was automatically generated by 'stack init'
|
||||
#
|
||||
# Some commonly used options have been documented as comments in this file.
|
||||
# For advanced use and comprehensive documentation of the format, please see:
|
||||
# https://docs.haskellstack.org/en/stable/yaml_configuration/
|
||||
|
||||
# A 'specific' Stackage snapshot or a compiler version.
|
||||
# A snapshot resolver dictates the compiler version and the set of packages
|
||||
# to be used for project dependencies. For example:
|
||||
#
|
||||
# snapshot: lts-22.28
|
||||
# snapshot: nightly-2024-07-05
|
||||
# snapshot: ghc-9.6.6
|
||||
#
|
||||
# The location of a snapshot can be provided as a file or url. Stack assumes
|
||||
# a snapshot provided as a file might change, whereas a url resource does not.
|
||||
#
|
||||
# snapshot: ./custom-snapshot.yaml
|
||||
# snapshot: https://example.com/snapshots/2024-01-01.yaml
|
||||
snapshot:
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/22/41.yaml
|
||||
|
||||
# User packages to be built.
|
||||
# Various formats can be used as shown in the example below.
|
||||
#
|
||||
# packages:
|
||||
# - some-directory
|
||||
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
|
||||
# subdirs:
|
||||
# - auto-update
|
||||
# - wai
|
||||
packages:
|
||||
- .
|
||||
# Dependency packages to be pulled from upstream that are not in the snapshot.
|
||||
# These entries can reference officially published versions as well as
|
||||
# forks / in-progress versions pinned to a git hash. For example:
|
||||
#
|
||||
# extra-deps:
|
||||
# - acme-missiles-0.3
|
||||
# - git: https://github.com/commercialhaskell/stack.git
|
||||
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
|
||||
#
|
||||
# extra-deps: []
|
||||
|
||||
# Override default flag values for project packages and extra-deps
|
||||
# flags: {}
|
||||
|
||||
# Extra package databases containing global packages
|
||||
# extra-package-dbs: []
|
||||
|
||||
# Control whether we use the GHC we find on the path
|
||||
# system-ghc: true
|
||||
#
|
||||
# Require a specific version of Stack, using version ranges
|
||||
# require-stack-version: -any # Default
|
||||
# require-stack-version: ">=3.1"
|
||||
#
|
||||
# Override the architecture used by Stack, especially useful on Windows
|
||||
# arch: i386
|
||||
# arch: x86_64
|
||||
#
|
||||
# Extra directories used by Stack for building
|
||||
# extra-include-dirs: [/path/to/dir]
|
||||
# extra-lib-dirs: [/path/to/dir]
|
||||
#
|
||||
# Allow a newer minor version of GHC than the snapshot specifies
|
||||
# compiler-check: newer-minor
|
||||
13
lab3/stack.yaml.lock
Normal file
@@ -0,0 +1,13 @@
|
||||
# This file was autogenerated by Stack.
|
||||
# You should not edit this file by hand.
|
||||
# For more information, please see the documentation at:
|
||||
# https://docs.haskellstack.org/en/stable/lock_files
|
||||
|
||||
packages: []
|
||||
snapshots:
|
||||
- completed:
|
||||
sha256: 1e32b51d9082fdf6f3bd92accc9dfffd4ddaf406404427fb10bf76d2bc03cbbb
|
||||
size: 720263
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/22/41.yaml
|
||||
original:
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/22/41.yaml
|
||||
BIN
lab3/task.jpg
Normal file
|
After Width: | Height: | Size: 185 KiB |
25
lab3/task.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
Практическое задание 3. Обработка файлов в Haskell
|
||||
№ 20
|
||||
|
||||
Создать проект в stack. Все чистые функции записать в библиотеку Lib.hs и ограничить
|
||||
доступ к вспомогательным функциям. Использовать do-нотацию для работы с внешними
|
||||
файлами. Найти портрет указанного человека:
|
||||
Дойч, Дэвид
|
||||
|
||||
Перевести изображение в формат .bmp (24-разрядный), при необходимости изменить
|
||||
ширину и высоту изображения без искажений. Сохранить в файл формата .txt фрагмент
|
||||
биографии (не менее 1000 символов без пробелов, текст не должен обрываться на середине
|
||||
слова или предложения). Закодировать текст в изображение методом:
|
||||
Шифром Цезаря. Смещение задается пользователем
|
||||
|
||||
Ключ к шифру записывается в имя файла. Написать функцию расшифровывающую текст
|
||||
из изображения используя ключ из имени файла и сохраняющую результат в отдельный
|
||||
текстовый файл.
|
||||
Создать функции шифрующие текст в последний бит каждого байта, последние два бита
|
||||
каждого байта, …, все биты в байте. В отчете привести примеры искажений изображения.
|
||||
|
||||
Задача со звездочкой*: Разобрать сложный формат данных с использованием библиотек
|
||||
доступных в Hackage. Зашифровать и расшифровать данные.
|
||||
|
||||
Задача с двумя звездочками**: Разобрать сложный формат данных самостоятельно
|
||||
используя только стандартные библиотеки. Зашифровать и расшифровать данные.
|
||||
2
lab3/test/Spec.hs
Normal file
@@ -0,0 +1,2 @@
|
||||
main :: IO ()
|
||||
main = putStrLn "Test suite not yet implemented"
|
||||
3
lab4/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.stack-work/
|
||||
*~
|
||||
!task.txt
|
||||
11
lab4/CHANGELOG.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Changelog for `lab4`
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to the
|
||||
[Haskell Package Versioning Policy](https://pvp.haskell.org/).
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 0.1.0.0 - YYYY-MM-DD
|
||||
26
lab4/LICENSE
Normal file
@@ -0,0 +1,26 @@
|
||||
Copyright 2024 Author name here
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
1
lab4/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# lab4
|
||||
2
lab4/Setup.hs
Normal file
@@ -0,0 +1,2 @@
|
||||
import Distribution.Simple
|
||||
main = defaultMain
|
||||
14
lab4/app/Main.hs
Normal file
@@ -0,0 +1,14 @@
|
||||
module Main (main) where
|
||||
|
||||
import Lib
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
putStrLn $ "Примеры работы `isCongruent`"
|
||||
putStrLn $ "isCongruent 10 12 2: " ++ show (isCongruent 10 12 2)
|
||||
putStrLn $ "isCongruent 10 11 2: " ++ show (isCongruent 10 11 2)
|
||||
|
||||
putStrLn $ "\nПример работы `filterByPredicate`"
|
||||
let predicate x = x > 5
|
||||
putStrLn $ "filterByPredicate (>5) [1, 6, 3, 7, 2]: "
|
||||
++ show (filterByPredicate predicate [1 :: Int, 6, 3, 7, 2])
|
||||
68
lab4/lab4.cabal
Normal file
@@ -0,0 +1,68 @@
|
||||
cabal-version: 2.2
|
||||
|
||||
-- This file has been generated from package.yaml by hpack version 0.37.0.
|
||||
--
|
||||
-- see: https://github.com/sol/hpack
|
||||
|
||||
name: lab4
|
||||
version: 0.1.0.0
|
||||
description: Please see the README on GitHub at <https://github.com/githubuser/lab4#readme>
|
||||
homepage: https://github.com/githubuser/lab4#readme
|
||||
bug-reports: https://github.com/githubuser/lab4/issues
|
||||
author: Author name here
|
||||
maintainer: example@example.com
|
||||
copyright: 2024 Author name here
|
||||
license: BSD-3-Clause
|
||||
license-file: LICENSE
|
||||
build-type: Simple
|
||||
extra-source-files:
|
||||
README.md
|
||||
CHANGELOG.md
|
||||
|
||||
source-repository head
|
||||
type: git
|
||||
location: https://github.com/githubuser/lab4
|
||||
|
||||
library
|
||||
exposed-modules:
|
||||
Lib
|
||||
other-modules:
|
||||
Paths_lab4
|
||||
autogen-modules:
|
||||
Paths_lab4
|
||||
hs-source-dirs:
|
||||
src
|
||||
ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints
|
||||
build-depends:
|
||||
base >=4.7 && <5
|
||||
default-language: Haskell2010
|
||||
|
||||
executable lab4-exe
|
||||
main-is: Main.hs
|
||||
other-modules:
|
||||
Paths_lab4
|
||||
autogen-modules:
|
||||
Paths_lab4
|
||||
hs-source-dirs:
|
||||
app
|
||||
ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N
|
||||
build-depends:
|
||||
base >=4.7 && <5
|
||||
, lab4
|
||||
default-language: Haskell2010
|
||||
|
||||
test-suite lab4-test
|
||||
type: exitcode-stdio-1.0
|
||||
main-is: Spec.hs
|
||||
other-modules:
|
||||
Paths_lab4
|
||||
autogen-modules:
|
||||
Paths_lab4
|
||||
hs-source-dirs:
|
||||
test
|
||||
ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N
|
||||
build-depends:
|
||||
QuickCheck
|
||||
, base >=4.7 && <5
|
||||
, lab4
|
||||
default-language: Haskell2010
|
||||
60
lab4/package.yaml
Normal file
@@ -0,0 +1,60 @@
|
||||
name: lab4
|
||||
version: 0.1.0.0
|
||||
github: "githubuser/lab4"
|
||||
license: BSD-3-Clause
|
||||
author: "Author name here"
|
||||
maintainer: "example@example.com"
|
||||
copyright: "2024 Author name here"
|
||||
|
||||
extra-source-files:
|
||||
- README.md
|
||||
- CHANGELOG.md
|
||||
|
||||
# Metadata used when publishing your package
|
||||
# synopsis: Short description of your package
|
||||
# category: Web
|
||||
|
||||
# To avoid duplicated efforts in documentation and dealing with the
|
||||
# complications of embedding Haddock markup inside cabal files, it is
|
||||
# common to point users to the README.md file.
|
||||
description: Please see the README on GitHub at <https://github.com/githubuser/lab4#readme>
|
||||
|
||||
dependencies:
|
||||
- base >= 4.7 && < 5
|
||||
|
||||
ghc-options:
|
||||
- -Wall
|
||||
- -Wcompat
|
||||
- -Widentities
|
||||
- -Wincomplete-record-updates
|
||||
- -Wincomplete-uni-patterns
|
||||
- -Wmissing-export-lists
|
||||
- -Wmissing-home-modules
|
||||
- -Wpartial-fields
|
||||
- -Wredundant-constraints
|
||||
|
||||
library:
|
||||
source-dirs: src
|
||||
|
||||
executables:
|
||||
lab4-exe:
|
||||
main: Main.hs
|
||||
source-dirs: app
|
||||
ghc-options:
|
||||
- -threaded
|
||||
- -rtsopts
|
||||
- -with-rtsopts=-N
|
||||
dependencies:
|
||||
- lab4
|
||||
|
||||
tests:
|
||||
lab4-test:
|
||||
main: Spec.hs
|
||||
source-dirs: test
|
||||
ghc-options:
|
||||
- -threaded
|
||||
- -rtsopts
|
||||
- -with-rtsopts=-N
|
||||
dependencies:
|
||||
- lab4
|
||||
- QuickCheck
|
||||
5
lab4/report/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
**/*
|
||||
!.gitignore
|
||||
!report.tex
|
||||
!img
|
||||
!img/*
|
||||
BIN
lab4/report/img/main.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
lab4/report/img/test.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
341
lab4/report/report.tex
Normal file
@@ -0,0 +1,341 @@
|
||||
\documentclass[a4paper, final]{article}
|
||||
%\usepackage{literat} % Нормальные шрифты
|
||||
\usepackage[14pt]{extsizes} % для того чтобы задать нестандартный 14-ый размер шрифта
|
||||
\usepackage{tabularx}
|
||||
\usepackage[T2A]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[russian]{babel}
|
||||
\usepackage{amsmath}
|
||||
\usepackage[left=25mm, top=20mm, right=20mm, bottom=20mm, footskip=10mm]{geometry}
|
||||
\usepackage{ragged2e} %для растягивания по ширине
|
||||
\usepackage{setspace} %для межстрочного интервала
|
||||
\usepackage{moreverb} %для работы с листингами
|
||||
\usepackage{indentfirst} % для абзацного отступа
|
||||
\usepackage{moreverb} %для печати в листинге исходного кода программ
|
||||
\usepackage{pdfpages} %для вставки других pdf файлов
|
||||
\usepackage{tikz}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{afterpage}
|
||||
\usepackage{longtable}
|
||||
\usepackage{float}
|
||||
|
||||
|
||||
|
||||
% \usepackage[paper=A4,DIV=12]{typearea}
|
||||
\usepackage{pdflscape}
|
||||
% \usepackage{lscape}
|
||||
|
||||
\usepackage{array}
|
||||
\usepackage{multirow}
|
||||
|
||||
\renewcommand\verbatimtabsize{4\relax}
|
||||
\renewcommand\listingoffset{0.2em} %отступ от номеров строк в листинге
|
||||
\renewcommand{\arraystretch}{1.4} % изменяю высоту строки в таблице
|
||||
\usepackage[font=small, singlelinecheck=false, justification=centering, format=plain, labelsep=period]{caption} %для настройки заголовка таблицы
|
||||
\usepackage{listings} %листинги
|
||||
\usepackage{xcolor} % цвета
|
||||
\usepackage{hyperref}% для гиперссылок
|
||||
\usepackage{enumitem} %для перечислений
|
||||
|
||||
% Настраиваем листинги, чтобы они использовали счётчик figure
|
||||
% \AtBeginDocument{
|
||||
% \renewcommand{\thelstlisting}{\thefigure} % Листинги используют тот же счетчик, что и рисунки
|
||||
% \renewcommand{\lstlistingname}{Рис.} % Меняем подпись на "Рисунок"
|
||||
% }
|
||||
|
||||
% Автоматически увеличиваем счетчик figure перед каждым листингом
|
||||
% \let\oldlstlisting\lstlisting
|
||||
% \renewcommand{\lstlisting}[1][]{%
|
||||
% \refstepcounter{figure}% Увеличиваем счетчик figure
|
||||
% \oldlstlisting[#1]% Вызываем оригинальную команду lstlisting
|
||||
% }
|
||||
|
||||
\newcommand{\specialcell}[2][l]{\begin{tabular}[#1]{@{}l@{}}#2\end{tabular}}
|
||||
|
||||
|
||||
\setlist[enumerate,itemize]{leftmargin=1.2cm} %отступ в перечислениях
|
||||
|
||||
\hypersetup{colorlinks,
|
||||
allcolors=[RGB]{010 090 200}} %красивые гиперссылки (не красные)
|
||||
|
||||
% подгружаемые языки — подробнее в документации listings (это всё для листингов)
|
||||
\lstloadlanguages{ Haskell}
|
||||
% включаем кириллицу и добавляем кое−какие опции
|
||||
\lstset{tabsize=2,
|
||||
breaklines,
|
||||
basicstyle=\footnotesize,
|
||||
columns=fullflexible,
|
||||
flexiblecolumns,
|
||||
numbers=left,
|
||||
numberstyle={\footnotesize},
|
||||
keywordstyle=\color{blue},
|
||||
inputencoding=cp1251,
|
||||
extendedchars=true
|
||||
}
|
||||
\lstdefinelanguage{MyC}{
|
||||
language=Haskell,
|
||||
% ndkeywordstyle=\color{darkgray}\bfseries,
|
||||
% identifierstyle=\color{black},
|
||||
% morecomment=[n]{/**}{*/},
|
||||
% commentstyle=\color{blue}\ttfamily,
|
||||
% stringstyle=\color{red}\ttfamily,
|
||||
% morestring=[b]",
|
||||
% showstringspaces=false,
|
||||
% morecomment=[l][\color{gray}]{//},
|
||||
keepspaces=true,
|
||||
escapechar=\%,
|
||||
texcl=true
|
||||
}
|
||||
|
||||
\textheight=24cm % высота текста
|
||||
\textwidth=16cm % ширина текста
|
||||
\oddsidemargin=0pt % отступ от левого края
|
||||
\topmargin=-1.5cm % отступ от верхнего края
|
||||
\parindent=24pt % абзацный отступ
|
||||
\parskip=5pt % интервал между абзацами
|
||||
\tolerance=2000 % терпимость к "жидким" строкам
|
||||
\flushbottom % выравнивание высоты страниц
|
||||
|
||||
|
||||
% Настройка листингов
|
||||
\lstset{
|
||||
language=Haskell,
|
||||
extendedchars=\true,
|
||||
inputencoding=utf8,
|
||||
keepspaces=true,
|
||||
captionpos=t,
|
||||
}
|
||||
|
||||
\begin{document} % начало документа
|
||||
|
||||
|
||||
|
||||
% НАЧАЛО ТИТУЛЬНОГО ЛИСТА
|
||||
\begin{center}
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ\\
|
||||
федеральное государственное автономное образовательное учреждение высшего образования «Санкт-Петербургский политехнический университет Петра Великого»\\[10pt]}
|
||||
\normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt]
|
||||
\normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt]
|
||||
\normalsize{Направление: 02.03.01 <<Математика и компьютерные науки>>}\\
|
||||
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\large{Отчет по лабораторной работе №4}\\
|
||||
\large{по дисциплине}\\
|
||||
\large{<<Функциональное программирование>>}\\
|
||||
\large{Вариант 12}\\
|
||||
\hfill \break
|
||||
|
||||
% \hfill \break
|
||||
% \hfill \break
|
||||
\end{center}
|
||||
|
||||
\small{
|
||||
\begin{tabular}{lrrl}
|
||||
\!\!\!Студент, & \hspace{2cm} & & \\
|
||||
\!\!\!группы 5130201/20102 & \hspace{2cm} & \underline{\hspace{3cm}} &Тищенко А. А. \\\\
|
||||
\!\!\!Преподаватель,\\ \hspace{-5pt}к. т. н., доц. & \hspace{2cm} & \underline{\hspace{3cm}} & Моторин Д. Е. \\\\
|
||||
&&\hspace{4cm}
|
||||
\end{tabular}
|
||||
\begin{flushright}
|
||||
<<\underline{\hspace{1cm}}>>\underline{\hspace{2.5cm}} 2024г.
|
||||
\end{flushright}
|
||||
}
|
||||
|
||||
\hfill \break
|
||||
% \hfill \break
|
||||
\begin{center} \small{Санкт-Петербург, 2024} \end{center}
|
||||
\thispagestyle{empty} % выключаем отображение номера для этой страницы
|
||||
|
||||
% КОНЕЦ ТИТУЛЬНОГО ЛИСТА
|
||||
\newpage
|
||||
|
||||
\tableofcontents
|
||||
|
||||
|
||||
\newpage
|
||||
\section {Постановка задачи}
|
||||
Для выполнения лабораторной работы необходимо было сделать следующее. Создать проект в \texttt{stack}. Функции записать в библиотеку \texttt{Lib.hs} и ограничить доступ к вспомогательным функциям. Тесты записать в \texttt{Spec.hs}.
|
||||
|
||||
\textbf{Функция 1:} Напишите функцию проверки равенства по модулю \texttt{isCongruent :: Int -> Int -> Int -> Bool}, которая проверяет, равны ли два числа по модулю третьего числа. Используя \texttt{QuickCheck}, проверьте следующие свойства:
|
||||
\begin{enumerate}
|
||||
\item Если два числа равны по модулю, то их разность делится на модуль:
|
||||
\texttt{isCongruent a b m == (modulus (a - b) m == 0)}.
|
||||
\item Равенство по модулю является симметричным:
|
||||
\texttt{isCongruent a b m == isCongruent b a m}.
|
||||
\item Если одно число равно другому, то они равны по любому модулю:
|
||||
если \texttt{a == b}, то \texttt{isCongruent a b m == True}.
|
||||
\end{enumerate}
|
||||
|
||||
\textbf{Функция 2:} Напишите функцию \texttt{filterByPredicate :: (a -> Bool) -> [a] -> [a]}, которая фильтрует элементы списка по заданному предикату. Используя \texttt{QuickCheck}, проверьте следующие свойства:
|
||||
\begin{enumerate}
|
||||
\item Все элементы результата удовлетворяют предикату: для каждого элемента \texttt{x} из результата должно выполняться условие: \texttt{predicate x == True}.
|
||||
\item Длина результата не превышает длину исходного списка:
|
||||
\texttt{length (filterByPredicate predicate xs) <= length xs}.
|
||||
\item Если предикат всегда возвращает \texttt{True}, то результат совпадает с исходным списком:
|
||||
если \texttt{predicate x == True} для всех \texttt{x}, то \texttt{filterByPredicate predicate xs == xs}.
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
\newpage
|
||||
\section {Особенности реализации}
|
||||
\subsection{Функция isCongruent}
|
||||
|
||||
Код функции для проверки числовой конгруэнтности представлен в листинге~\ref{lst:is-congruent}. Функция \texttt{isCongruent} принимает три числа: \texttt{a}, \texttt{b} и \texttt{d}. Она возвращает \texttt{True}, если остатки от деления \texttt{a} и \texttt{b} на \texttt{d} равны, и \texttt{False} в противном случае.
|
||||
|
||||
\begin{lstlisting}[caption={Функция для проверки числовой конгруэнтности.}, label={lst:is-congruent}]
|
||||
isCongruent :: Int -> Int -> Int -> Bool
|
||||
isCongruent a b d = a `mod` d == b `mod` d
|
||||
\end{lstlisting}
|
||||
|
||||
\subsection{Функция filterByPredicate}
|
||||
|
||||
Код функции для фильтрации элементов списка на основе предиката представлен в листинге~\ref{lst:filter-by-predicate}. Функция \texttt{filterByPredicate} принимает предикат (\texttt{predicate}) и список (\texttt{[a]}). Она возвращает новый список, содержащий только те элементы исходного списка, для которых предикат возвращает \texttt{True}.
|
||||
|
||||
\begin{lstlisting}[caption={Функция для фильтрации списка по предикату.}, label={lst:filter-by-predicate}]
|
||||
filterByPredicate :: (a -> Bool) -> [a] -> [a]
|
||||
filterByPredicate _ [] = []
|
||||
filterByPredicate predicate (x:xs)
|
||||
| predicate x = x : filteredTail
|
||||
| otherwise = filteredTail
|
||||
where
|
||||
filteredTail = filterByPredicate predicate xs
|
||||
\end{lstlisting}
|
||||
|
||||
|
||||
\subsection{Тесты для функции isCongruent}
|
||||
|
||||
Для тестирования функции \texttt{isCongruent} использовались свойства, проверяемые с использованием библиотеки QuickCheck. Ниже приведены описания тестов, представленных в листинге~\ref{lst:congruent-tests}.
|
||||
|
||||
\begin{itemize}
|
||||
\item \texttt{propCongruentDifference}: Проверяет, что два числа конгруэнтны по модулю \texttt{m}, если и только если разность этих чисел делится на \texttt{m} без остатка.
|
||||
\item \texttt{propCongruentSymmetric}: Проверяет симметричность конгруэнтности, то есть, если \texttt{a} конгруэнтно \texttt{b}, то \texttt{b} конгруэнтно \texttt{a}.
|
||||
\item \texttt{propCongruentEqualNumbers}: Проверяет, что любое число \texttt{a} всегда конгруэнтно самому себе по любому ненулевому модулю.
|
||||
\end{itemize}
|
||||
|
||||
\begin{lstlisting}[caption={Тесты для функции \texttt{isCongruent} с использованием QuickCheck.}, label={lst:congruent-tests}]
|
||||
propCongruentDifference :: Int -> Int -> Int -> Property
|
||||
propCongruentDifference a b m =
|
||||
m /= 0 ==> isCongruent a b m == ((a - b) `mod` m == 0)
|
||||
|
||||
propCongruentSymmetric :: Int -> Int -> Int -> Property
|
||||
propCongruentSymmetric a b m =
|
||||
m /= 0 ==> isCongruent a b m == isCongruent b a m
|
||||
|
||||
propCongruentEqualNumbers :: Int -> Int -> Property
|
||||
propCongruentEqualNumbers a m =
|
||||
m /= 0 ==> isCongruent a a m == True
|
||||
\end{lstlisting}
|
||||
|
||||
|
||||
\subsection{Тесты для функции filterByPredicate}
|
||||
|
||||
Для тестирования функции \texttt{filterByPredicate} использовались свойства, проверяемые с использованием библиотеки QuickCheck. Описание тестов приведено в листинге~\ref{lst:filter-tests}.
|
||||
|
||||
\begin{itemize}
|
||||
\item \texttt{propFilterByPredicateSatisfiesPredicate}: Проверяет, что все элементы результирующего списка удовлетворяют переданному предикату.
|
||||
\item \texttt{propFilterByPredicateLength}: Проверяет, что длина результирующего списка не превышает длину исходного списка.
|
||||
\item \texttt{propFilterByPredicateAlwaysTrue}: Проверяет, что если предикат всегда возвращает \texttt{True}, результирующий список совпадает с исходным.
|
||||
\end{itemize}
|
||||
|
||||
\begin{lstlisting}[caption={Тесты для функции \texttt{filterByPredicate} с использованием QuickCheck.}, label={lst:filter-tests}]
|
||||
propFilterByPredicateSatisfiesPredicate :: Fun Int Bool -> [Int] -> Bool
|
||||
propFilterByPredicateSatisfiesPredicate (Fun _ predicate) xs =
|
||||
all predicate (filterByPredicate predicate xs)
|
||||
|
||||
propFilterByPredicateLength :: Fun Int Bool -> [Int] -> Bool
|
||||
propFilterByPredicateLength (Fun _ predicate) xs =
|
||||
length (filterByPredicate predicate xs) <= length xs
|
||||
|
||||
propFilterByPredicateAlwaysTrue :: [Int] -> Bool
|
||||
propFilterByPredicateAlwaysTrue xs =
|
||||
filterByPredicate (\_ -> True) xs == xs
|
||||
\end{lstlisting}
|
||||
|
||||
В листинге~\ref{lst:broken1} представлена реализация функции \texttt{filterByPredicate}, которая не пройдёт ни один из перечисленных тестов. А в листинге~\ref{lst:broken2} представлена реализация, которая не пройдёт два из трёх тестов, первый и третий.
|
||||
|
||||
\begin{lstlisting}[caption={Пример реализации функции filterByPredicate, которая не пройдёт ни один тест.}, label={lst:broken1}]
|
||||
filterByPredicate :: (a -> Bool) -> [a] -> [a]
|
||||
filterByPredicate _ [] = []
|
||||
filterByPredicate predicate (x:xs)
|
||||
| not $ predicate x = x : x : filteredTail
|
||||
| otherwise = x : x : filteredTail
|
||||
where
|
||||
filteredTail = filterByPredicate predicate xs
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{lstlisting}[caption={Пример реализации функции filterByPredicate, которая не пройдёт первый и третий тесты.}, label={lst:broken2}]
|
||||
filterByPredicate :: (a -> Bool) -> [a] -> [a]
|
||||
filterByPredicate _ [] = []
|
||||
filterByPredicate predicate (x:xs)
|
||||
| not $ predicate x = x : filteredTail
|
||||
| otherwise = filteredTail
|
||||
where
|
||||
filteredTail = filterByPredicate predicate xs
|
||||
\end{lstlisting}
|
||||
|
||||
\subsection{Запуск тестов}
|
||||
|
||||
Для выполнения всех тестов в проекте создан файл \texttt{Spec.hs}, в котором определена функция \texttt{main}. Она последовательно запускает все тесты, используя библиотеку \texttt{QuickCheck}. Код функции \texttt{main} приведён в листинге~\ref{lst:test-main}.
|
||||
|
||||
Функция \texttt{quickCheck} используется для автоматического тестирования свойств. Она генерирует случайные входные данные, проверяет выполнение свойства на каждом из них и сообщает о результатах. В случае провала теста \texttt{quickCheck} предоставляет пример данных, на которых свойство не выполняется.
|
||||
|
||||
Для сборки проекта и выполнения всех тестов достаточно выполнить команду \texttt{stack test}.
|
||||
|
||||
\begin{lstlisting}[caption={Функция \texttt{main} для запуска тестов.}, label={lst:test-main}]
|
||||
main :: IO ()
|
||||
main = do
|
||||
quickCheck propCongruentDifference
|
||||
quickCheck propCongruentSymmetric
|
||||
quickCheck propCongruentEqualNumbers
|
||||
|
||||
quickCheck propFilterByPredicateSatisfiesPredicate
|
||||
quickCheck propFilterByPredicateLength
|
||||
quickCheck propFilterByPredicateAlwaysTrue
|
||||
\end{lstlisting}
|
||||
|
||||
|
||||
|
||||
\newpage
|
||||
\section {Результаты работы программы}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.6\linewidth]{img/main.png}
|
||||
\caption{Результаты работы программы.}
|
||||
\label{fig:main}
|
||||
\end{figure}
|
||||
|
||||
Сама программа лишь выводит примеры работы функций \texttt{isCongruent} и \texttt{filterByPredicate}. Результаты её запуска представлены на Рис.~\ref{fig:main}.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.6\linewidth]{img/test.png}
|
||||
\caption{Результаты запуска тестов.}
|
||||
\label{fig:test}
|
||||
\end{figure}
|
||||
|
||||
Результаты запуска тестов с помощью команды \texttt{stack test} представлены на Рис.~\ref{fig:test}.
|
||||
|
||||
|
||||
|
||||
\newpage
|
||||
\section*{Заключение}
|
||||
\addcontentsline{toc}{section}{Заключение}
|
||||
В данной работе был создан проект с использованием \texttt{stack}, включающий разработку и тестирование двух функций: проверки равенства по модулю и фильтрации списка по предикату. Для каждой функции были написаны три теста. Тесты запускаются на сгенерированных данных с помощью \texttt{QuickCheck}. Они подтверждают корректность реализации этих функций. В ходе выполнения лабораторной работы были получены базовые знания об автоматическом тестировании программ, написанных на языке Haskell.
|
||||
|
||||
|
||||
\newpage
|
||||
\section*{Список литературы}
|
||||
\addcontentsline{toc}{section}{Список литературы}
|
||||
|
||||
\vspace{-1.5cm}
|
||||
\begin{thebibliography}{0}
|
||||
\bibitem{JuicyPixels}
|
||||
Hackage -- QuickCheck: Automatic testing of Haskell programs, URL: \url{https://hackage.haskell.org/package/QuickCheck}, Дата обращения: 19.11.2024
|
||||
\end{thebibliography}
|
||||
\end{document}
|
||||
16
lab4/src/Lib.hs
Normal file
@@ -0,0 +1,16 @@
|
||||
module Lib
|
||||
( isCongruent,
|
||||
filterByPredicate
|
||||
) where
|
||||
|
||||
isCongruent :: Int -> Int -> Int -> Bool
|
||||
isCongruent a b d = a `mod` d == b `mod` d
|
||||
|
||||
|
||||
filterByPredicate :: (a -> Bool) -> [a] -> [a]
|
||||
filterByPredicate _ [] = []
|
||||
filterByPredicate predicate (x:xs)
|
||||
| predicate x = x : filteredTail
|
||||
| otherwise = filteredTail
|
||||
where
|
||||
filteredTail = filterByPredicate predicate xs
|
||||
67
lab4/stack.yaml
Normal file
@@ -0,0 +1,67 @@
|
||||
# This file was automatically generated by 'stack init'
|
||||
#
|
||||
# Some commonly used options have been documented as comments in this file.
|
||||
# For advanced use and comprehensive documentation of the format, please see:
|
||||
# https://docs.haskellstack.org/en/stable/yaml_configuration/
|
||||
|
||||
# A 'specific' Stackage snapshot or a compiler version.
|
||||
# A snapshot resolver dictates the compiler version and the set of packages
|
||||
# to be used for project dependencies. For example:
|
||||
#
|
||||
# snapshot: lts-22.28
|
||||
# snapshot: nightly-2024-07-05
|
||||
# snapshot: ghc-9.6.6
|
||||
#
|
||||
# The location of a snapshot can be provided as a file or url. Stack assumes
|
||||
# a snapshot provided as a file might change, whereas a url resource does not.
|
||||
#
|
||||
# snapshot: ./custom-snapshot.yaml
|
||||
# snapshot: https://example.com/snapshots/2024-01-01.yaml
|
||||
snapshot:
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/22/43.yaml
|
||||
|
||||
# User packages to be built.
|
||||
# Various formats can be used as shown in the example below.
|
||||
#
|
||||
# packages:
|
||||
# - some-directory
|
||||
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
|
||||
# subdirs:
|
||||
# - auto-update
|
||||
# - wai
|
||||
packages:
|
||||
- .
|
||||
# Dependency packages to be pulled from upstream that are not in the snapshot.
|
||||
# These entries can reference officially published versions as well as
|
||||
# forks / in-progress versions pinned to a git hash. For example:
|
||||
#
|
||||
# extra-deps:
|
||||
# - acme-missiles-0.3
|
||||
# - git: https://github.com/commercialhaskell/stack.git
|
||||
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
|
||||
#
|
||||
# extra-deps: []
|
||||
|
||||
# Override default flag values for project packages and extra-deps
|
||||
# flags: {}
|
||||
|
||||
# Extra package databases containing global packages
|
||||
# extra-package-dbs: []
|
||||
|
||||
# Control whether we use the GHC we find on the path
|
||||
# system-ghc: true
|
||||
#
|
||||
# Require a specific version of Stack, using version ranges
|
||||
# require-stack-version: -any # Default
|
||||
# require-stack-version: ">=3.1"
|
||||
#
|
||||
# Override the architecture used by Stack, especially useful on Windows
|
||||
# arch: i386
|
||||
# arch: x86_64
|
||||
#
|
||||
# Extra directories used by Stack for building
|
||||
# extra-include-dirs: [/path/to/dir]
|
||||
# extra-lib-dirs: [/path/to/dir]
|
||||
#
|
||||
# Allow a newer minor version of GHC than the snapshot specifies
|
||||
# compiler-check: newer-minor
|
||||
13
lab4/stack.yaml.lock
Normal file
@@ -0,0 +1,13 @@
|
||||
# This file was autogenerated by Stack.
|
||||
# You should not edit this file by hand.
|
||||
# For more information, please see the documentation at:
|
||||
# https://docs.haskellstack.org/en/stable/lock_files
|
||||
|
||||
packages: []
|
||||
snapshots:
|
||||
- completed:
|
||||
sha256: 08bd13ce621b41a8f5e51456b38d5b46d7783ce114a50ab604d6bbab0d002146
|
||||
size: 720271
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/22/43.yaml
|
||||
original:
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/22/43.yaml
|
||||
BIN
lab4/task.jpg
Normal file
|
After Width: | Height: | Size: 162 KiB |
19
lab4/task.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
Практическое задание 4. Тестирование в Haskell
|
||||
№ 12
|
||||
|
||||
Создать проект в stack. Функции записать в библиотеку Lib.hs и ограничить доступ к вспомогательным функциям. Тесты записать в Spec.hs.
|
||||
|
||||
Функция 1: Напишите функцию проверки равенства по модулю isCongruent :: Int -> Int -> Int -> Bool, которая проверяет, равны ли два числа по модулю третьего числа. Используя QuickCheck, проверьте следующие свойства:
|
||||
|
||||
1. Если два числа равны по модулю, то их разность делится на модуль:
|
||||
isCongruent a b m == (modulus (a - b) m == 0).
|
||||
2. Равенство по модулю является симметричным:
|
||||
isCongruent a b m == isCongruent b a m.
|
||||
3. Если одно число равно другому, то они равны по любому модулю:
|
||||
если a == b, то isCongruent a b m == True.
|
||||
|
||||
Функция 2: Напишите функцию filterByPredicate :: (a -> Bool) -> [a] -> [a], которая фильтрует элементы списка по заданному предикату. Используя QuickCheck, проверьте следующие свойства:
|
||||
|
||||
1. Все элементы результата удовлетворяют предикату: для каждого элемента x из результата должно выполняться условие: predicate x == True.
|
||||
2. Длина результата не превышает длину исходного списка: length (filterByPredicate predicate xs) <= length xs.
|
||||
3. Если предикат всегда возвращает True, то результат совпадает с исходным списком: если predicate x == True для всех x, то filterByPredicate predicate xs == xs.
|
||||
43
lab4/test/Spec.hs
Normal file
@@ -0,0 +1,43 @@
|
||||
import Test.QuickCheck
|
||||
import Lib
|
||||
|
||||
-- Если два числа равны по модулю, то их разность делится на модуль
|
||||
propCongruentDifference :: Int -> Int -> Int -> Property
|
||||
propCongruentDifference a b m =
|
||||
m /= 0 ==> isCongruent a b m == ((a - b) `mod` m == 0)
|
||||
|
||||
-- Равенство по модулю является симметричным
|
||||
propCongruentSymmetric :: Int -> Int -> Int -> Property
|
||||
propCongruentSymmetric a b m =
|
||||
m /= 0 ==> isCongruent a b m == isCongruent b a m
|
||||
|
||||
-- Если одно число равно другому, то они равны по любому модулю
|
||||
propCongruentEqualNumbers :: Int -> Int -> Property
|
||||
propCongruentEqualNumbers a m =
|
||||
m /= 0 ==> isCongruent a a m == True
|
||||
|
||||
|
||||
-- Все элементы результата удовлетворяют предикату
|
||||
propFilterByPredicateSatisfiesPredicate :: Fun Int Bool -> [Int] -> Bool
|
||||
propFilterByPredicateSatisfiesPredicate (Fun _ predicate) xs =
|
||||
all predicate (filterByPredicate predicate xs)
|
||||
|
||||
-- Длина результата не превышает длину исходного списка
|
||||
propFilterByPredicateLength :: Fun Int Bool -> [Int] -> Bool
|
||||
propFilterByPredicateLength (Fun _ predicate) xs =
|
||||
length (filterByPredicate predicate xs) <= length xs
|
||||
|
||||
-- Если предикат всегда возвращает True, то результат совпадает с исходным списком
|
||||
propFilterByPredicateAlwaysTrue :: [Int] -> Bool
|
||||
propFilterByPredicateAlwaysTrue xs =
|
||||
filterByPredicate (\_ -> True) xs == xs
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
quickCheck propCongruentDifference
|
||||
quickCheck propCongruentSymmetric
|
||||
quickCheck propCongruentEqualNumbers
|
||||
|
||||
quickCheck propFilterByPredicateSatisfiesPredicate
|
||||
quickCheck propFilterByPredicateLength
|
||||
quickCheck propFilterByPredicateAlwaysTrue
|
||||