Skip to content

Commit

Permalink
Version 0.1, simulation friendly, written in modern Gleam
Browse files Browse the repository at this point in the history
  • Loading branch information
mrdimosthenis committed Nov 10, 2023
1 parent bcebf1c commit a2575ec
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 198 deletions.
12 changes: 7 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.0.0
- uses: erlef/setup-beam@v1.9.0
- uses: actions/checkout@v3
- uses: erlef/setup-beam@v1
with:
otp-version: "23.2"
gleam-version: "0.21.0"
- run: gleam format --check src test
otp-version: "26.0.2"
gleam-version: "0.32.4"
rebar3-version: "3"
# elixir-version: "1.15.4"
- run: gleam deps download
- run: gleam test
- run: gleam format --check src test
23 changes: 2 additions & 21 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,23 +1,4 @@
*.beam
*.iml
*.o
*.plt
*.swo
*.swp
*~
.erlang.cookie
.eunit
.idea
.rebar
.rebar3
_*
_build
docs
ebin
erl_crash.dump
gen
log
logs
rebar3.crashdump
/rebar.lock
*.ez
build
erl_crash.dump
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# minigen

A library for generating random data in the Erlang ecosystem, written in Gleam.
Pure random data generation library, appropriate for realistic simulations in the Erlang ecosystem.

## Installation

* For **Erlang** projects, add the dependency into `rebar.config`:

```erlang
{deps, [
{minigen, "0.0.3"}
{minigen, "0.1.0"}
]}.
```

Expand All @@ -17,7 +17,7 @@ A library for generating random data in the Erlang ecosystem, written in Gleam.
```elixir
defp deps do
[
{:minigen, "~> 0.0.3"}
{:minigen, "~> 0.1.0", manager: :rebar3}
]
end
```
Expand Down
21 changes: 7 additions & 14 deletions gleam.toml
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
name = "minigen"
version = "0.0.3"
licences = ["Apache-2.0"]
description = "A library for generating data in the Erlang ecosystem"

[docs]
links = [
{ title = 'GitHub', href = 'https://github.com/mrdimosthenis/minigen' }
]
version = "0.1.0"

[repository]
type = "github"
user = "mrdimosthenis"
repo = "minigen"
description = "Pure random data generation, appropriate for realistic simulations"
licences = ["Apache-2.0"]
repository = { type = "github", user = "mrdimosthenis", repo = "minigen" }
links = [{ title = 'GitHub', href = 'https://github.com/mrdimosthenis/minigen' }]

[dependencies]
gleam_stdlib = "~> 0.21"
gleam_stdlib = "~> 0.32"

[dev-dependencies]
gleeunit = "~> 0.6"
gleeunit = "~> 1.0"
11 changes: 0 additions & 11 deletions manifest.toml

This file was deleted.

133 changes: 53 additions & 80 deletions src/minigen.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
import gleam/float
import gleam/int
import gleam/list
import gleam/string
import gleam/pair
import gleam/result
import gleam/string
import minigen/interop

type Seed =
Expand Down Expand Up @@ -205,7 +204,7 @@ pub fn always(x: a) -> Generator(a) {
Generator(f)
}

/// Creates a generatoe for float values.
/// Creates a generator for float values.
/// By running it, we get a random float uniformly distributed between 0.0 (included) and 1.0 (excluded).
///
/// #### Erlang example
Expand Down Expand Up @@ -268,12 +267,10 @@ pub fn float() -> Generator(Float) {
/// ```
///
pub fn integer(n: Int) -> Generator(Int) {
float()
|> map(fn(x) {
x *. int.to_float(n)
|> float.floor
|> float.round
})
use x <- map(float())
x *. int.to_float(n)
|> float.floor
|> float.round
}

/// Creates a generator for boolean values.
Expand Down Expand Up @@ -304,8 +301,8 @@ pub fn integer(n: Int) -> Generator(Int) {
/// ```
///
pub fn boolean() -> Generator(Bool) {
float()
|> map(fn(x) { x <. 0.5 })
use x <- map(float())
x <. 0.5
}

/// Creates a generator that randomly selects an element from a list.
Expand Down Expand Up @@ -356,7 +353,7 @@ pub fn element_of_list(ls: List(a)) -> Generator(Result(a, Nil)) {
ls
|> list.length
|> integer
|> map(fn(n) { list.at(ls, n) })
|> map(list.at(ls, _))
}

/// Creates a generator that changes the order of the elements in a list.
Expand Down Expand Up @@ -386,49 +383,36 @@ pub fn element_of_list(ls: List(a)) -> Generator(Result(a, Nil)) {
/// ```
///
pub fn shuffled_list(ls: List(a)) -> Generator(List(a)) {
let move_to_edge = fn(acc_ls) {
case acc_ls {
[] -> always([])
[x] -> always([x])
_ ->
list.length(acc_ls) - 1
|> integer
|> map2(
integer(6),
fn(i, cs) {
let #(before, rest) = list.split(acc_ls, i + 1)
let #(elem, after) = list.split(rest, 1)
case cs {
0 -> list.flatten([elem, before, after])
1 -> list.flatten([elem, after, before])
2 -> list.flatten([before, elem, after])
3 -> list.flatten([before, after, elem])
4 -> list.flatten([after, elem, before])
5 -> list.flatten([after, before, elem])
}
},
)
case ls {
[] -> always([])
_ -> {
let gen =
ls
|> list.length
|> list.range(1)
|> list.map(integer)
|> sequence
{
use indexes <- map(gen)
use acc, i <- list.fold(indexes, #([], ls))
let #(selected_elems, rest_elems) = acc
let #(before, [chosen, ..after]) = list.split(rest_elems, i)
let next_selected_elems = list.prepend(selected_elems, chosen)
let next_rest_elems = list.append(before, after)
#(next_selected_elems, next_rest_elems)
}
|> map(pair.first)
}
}

list.fold(ls, always(ls), fn(acc, _) { then(acc, move_to_edge) })
}

fn number_graphemes() -> List(String) {
["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
}

fn lower_graphemes() -> List(String) {
fn graphemes() -> List(String) {
[
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p",
"q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
]
}

fn upper_graphemes() -> List(String) {
[
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P",
"Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
"p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D",
"E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S",
"T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7",
"8", "9",
]
}

Expand Down Expand Up @@ -460,56 +444,45 @@ fn upper_graphemes() -> List(String) {
/// ```
///
pub fn string(n: Int) -> Generator(String) {
3
|> integer
|> then(fn(cs) {
case cs {
0 -> number_graphemes()
1 -> lower_graphemes()
2 -> upper_graphemes()
}
|> element_of_list
graphemes()
|> element_of_list
|> map(fn(r) {
let assert Ok(c) = r
c
})
|> map(result.unwrap(_, ""))
|> list(n)
|> map(list.reverse)
|> map(list.fold(_, "", string.append))
|> map(string.join(_, ""))
}

fn list_help(
fn list_go(
gen: Generator(a),
seed: Seed,
ls: List(a),
n: Int,
) -> #(Seed, List(a)) {
case n < 1 {
True -> #(seed, ls)
False -> {
case n {
0 -> #(seed, ls)
_ -> {
let Generator(f) = gen
let #(next_seed, value) = f(seed)
let next_ls = list.append([value], ls)
list_help(gen, next_seed, next_ls, n - 1)
let next_ls = list.prepend(ls, value)
list_go(gen, next_seed, next_ls, n - 1)
}
}
}

pub fn list(gen: Generator(a), n: Int) -> Generator(List(a)) {
let f = fn(seed) { list_help(gen, seed, [], n) }
let f = fn(seed) { list_go(gen, seed, [], n) }
Generator(f)
}

pub fn sequence(gens: List(Generator(a))) -> Generator(List(a)) {
list.fold(
gens,
always([]),
fn(acc_gen, next_gen) {
then(
acc_gen,
fn(acc_ls) {
map(next_gen, fn(next_head) { list.append([next_head], acc_ls) })
},
)
},
)
{
use acc_gen, next_gen <- list.fold(gens, always([]))
use acc_ls <- then(acc_gen)
use next_head <- map(next_gen)
list.prepend(acc_ls, next_head)
}
|> map(list.reverse)
}
20 changes: 10 additions & 10 deletions src/minigen/interop.gleam
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
pub external type Algorithm
pub type Algorithm

pub external type State
pub type State

pub external fn default_algorithm() -> Algorithm =
"native_rand_mimigen" "default_algorithm"
@external(erlang, "native_rand_mimigen", "default_algorithm")
pub fn default_algorithm() -> Algorithm

pub external fn seed_s(Algorithm) -> State =
"rand" "seed_s"
@external(erlang, "rand", "seed_s")
pub fn seed_s(algorithm: Algorithm) -> State

pub external fn seed(Algorithm, Int) -> State =
"rand" "seed"
@external(erlang, "rand", "seed")
pub fn seed(algorithm: Algorithm, int: Int) -> State

pub external fn uniform_s(State) -> #(Float, State) =
"rand" "uniform_s"
@external(erlang, "rand", "uniform_s")
pub fn uniform_s(state: State) -> #(Float, State)
2 changes: 1 addition & 1 deletion src/native_rand_mimigen.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
-export([default_algorithm/0]).

default_algorithm() ->
exsss.
exs1024s.
6 changes: 3 additions & 3 deletions test/minigen/minigen/interop_test.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ pub fn uniform_s_test() {
interop.default_algorithm()
|> interop.seed(1000)
let #(x, new_state) = interop.uniform_s(init_state)
should.equal(x, 0.7109364198110805)
should.equal(x, 0.27586903946041397)
let #(y, _) = interop.uniform_s(init_state)
should.equal(y, 0.7109364198110805)
should.equal(y, 0.27586903946041397)
let #(z, _) = interop.uniform_s(new_state)
should.equal(z, 0.47372875562526207)
should.equal(z, 0.1952355138836377)
}
Loading

0 comments on commit a2575ec

Please sign in to comment.