Continuing on, since I had my State figured out, I next needed to figure out the Message, Update, and View sections. And got stuck. So, taking my own advice from when I was teaching mathematics, I attempted to simplify the problem once again. Leaving my solitaire card project behind for the moment, I pulled up the code for an older side project that was more of a lark than anything else. I had just installed a new CPU and wanted to see if I could write an app that would max it out. So, decided to identify prime numbers between any two given min and max limits. I used a brute force algorithm hoping it would tax the capabilities of my CPU. It didn’t, but was a fun & simple project. (I haven’t added multiple threads to try to use all the available CPU cores, but maybe some day??) Anyway, this app doesn’t use a GUI at all and is entirely terminal based. My goal was to learn how to use the ELM architecture. I succeeded, but would like to run my code past you guys to get your suggestions. Iced isn’t involved so far, but that is next step. Anyway, here is the original code before ELM in two sections (main.rs and lib.rs)……. main.rs is first:
/*
Find and print all prime numbers within a given range.
*/
use std::thread;
use std::thread::JoinHandle;
use coldprimes::numput::inpt_u64;
fn main() {
// region Input the range being searched
println!("\nWhat range of numbers do you wish to check for primes?");
println!("Please enter your starting number: ");
let mut start = inpt_u64();
let beginrange = start;
println!("Please enter your ending number: ");
let endrange = inpt_u64();
// endregion
let mut primeslist: Vec<u64> = Vec::new();
let mut numtup: (bool, u64);
let mut i = 0;
while start <= endrange {
numtup = porc(start);
if numtup.0 == true {
println!("{} is a prime number.", start);
primeslist.push(start); // Store the prime number in the vector.
i += 1;
}
start += 1;
if start % 10 == 0 {
// Nothing to do with finding primes. This is feedback.
println!("Working on {} and higher.", start);
}
}
println!(
"\nThere are {} primes between {} and {}. \n",
i, beginrange, endrange);
println!("Here they are: \n, {:?}", primeslist);
}
/*
Prime or Composite (porc())
Determine if a number is prime and return 'true' for primeness
along with a divisor of 1. If the number is not prime, then
return 'false' along with the first divisor encountered. Please
note that this algorithm uses a brute force tactic. There are
better solutions that should be explored.
*/ // porc() description -- Prime or Composite
pub fn porc(check4prime: u64) -> (bool, u64) {
if check4prime == 2 {
return (true, 1);
} // 2 is the only even prime. This ensures it returns as prime.
if check4prime % 2 == 0 {
return (false, 2);
} // If the number is even, it can't be prime (except for 2).
// Checking this here lets me increment i by 2 rather than 1,
// thus cutting in half the number of necessary iterations.
let mut i = 3;
while i < check4prime {
if check4prime % i == 0 {
return (false, i);
}
i += 2; // Skip the even numbers;
}
return (true, 1);
}
and here is the old lib.rs :
// Input, error check, and return numbers
// from standard input.
pub mod numput {
// Return a u64 from standard input.
pub fn inpt_u64() -> u64 {
loop {
let mut input = String::new();
std::io::stdin()
.read_line(&mut input)
.expect("Failed to read line.");
let input: u64 = match input.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("Please enter a numeric value. Thanks!");
continue;
}
};
break input;
}
}
}
Here is my new and improved version using the ELM format:
use coldprimes::{getprimes, Message};
fn main() {
let mut state = getprimes::new(); // Initialize the state.
// Initial View (Introduction)
println!("\n --- Prime Finder (Elm Architecture) --- \n");
// Start the message loop.
state.update(Message::Getmin);
state.update(Message::Getmax);
state.update(Message::CheckPrime(0)); // CheckPrime(0) is a dummy value.
// View the results
state.view();
}
and
use crate::numput::inpt_u64;
use crate::primes_functions::porc;
// region State
pub struct getprimes {
rangemin: u64,
rangemax: u64,
primeslist: Vec<u64>,
primescount: u64,
}
impl getprimes {
pub fn new() -> Self {
Self {
rangemin: 0,
rangemax: 0,
primeslist: Vec::new(),
primescount: 0,
}
}
}
// endregion
// region Messages
pub enum Message {
Getmin,
Getmax,
CheckPrime(u64),
}
// endregion
// region Update
impl getprimes {
pub fn update(&mut self, message: Message) {
match message {
Message::Getmin => {
println!("Please enter the starting number: ");
self.rangemin = inpt_u64();
},
Message::Getmax => {
println!("Please enter the ending number: ");
self.rangemax = inpt_u64();
},
Message::CheckPrime(num) => {
let mut use_tuple: (bool, u64);
// region Loop through the range to find primes.
let mut i = 0;
while self.rangemin <= self.rangemax {
use_tuple = porc(self.rangemin);
if use_tuple.0 == true {
println!("{} is a prime number.", self.rangemin);
self.primeslist.push(self.rangemin); // Store the prime number in the vector.
self.primescount += 1;
i += 1;
}
self.rangemin += 1;
// Provide feedback on progress.
if self.rangemin % 100 == 0 {
println!("Working on {} and higher.", self.rangemin);
}
}
// endregion
}
}
}
}
// endregion
// region View
impl getprimes {
pub fn view(&self) {
println!(
"\nThere are {} primes between {} and {}. \n",
self.primescount, self.rangemin, self.rangemax);
println!("Here they are: \n, {:?}", self.primeslist);
}
}
// endregion
/// A module containing functions related to prime number operations.
///
pub mod primes_functions {
/// # Prime or Composite (porc())
///
/// Determine if a number is prime and return 'true' for primeness
/// along with a divisor of 1. If the number is not prime, then
/// return 'false' along with the first divisor encountered. Please
/// note that this algorithm uses a brute force tactic.
///
pub fn porc(check4prime: u64) -> (bool, u64) {
// region Two is the only even prime. This ensures it returns as prime.
if check4prime == 2 {
return (true, 1);
}
// endregion
// region Check if the number is even.
if check4prime % 2 == 0 {
return (false, 2);
} // If the number is even, it can't be prime (except for 2).
// Checking this here lets me increment i by 2 rather than 1,
// thus cutting in half the number of necessary iterations.
// endregion
// region Loop through and check the odd numbers as divisors.
let mut i = 3;
// Could you break the loop when i == check4prime / 2? I think so.
while i < check4prime {
if check4prime % i == 0 {
return (false, i);
}
i += 2; // Skip the even numbers;
}
// endregion
return (true, 1);
}
}
/// Input, error check, and return numbers from standard input.
///
pub mod numput {
/// Return a u64 from standard input.
///
pub fn inpt_u64() -> u64 {
loop {
let mut input = String::new();
std::io::stdin()
.read_line(&mut input)
.expect("Failed to read line.");
let input: u64 = match input.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("Please enter a numeric value. Thanks!");
continue;
}
};
break input;
}
}
}
As you look at what I’ve done, please keep in mind that my goal with this code is to learn the ELM format. Adding the Iced GUI is my next step. I would love to hear your analysis and suggestions. Thanks.