Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
propensive committed Jun 28, 2024
0 parents commit a49ba2e
Show file tree
Hide file tree
Showing 9 changed files with 396 additions and 0 deletions.
19 changes: 19 additions & 0 deletions .github/workflows/admin.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Repo Admin
on:
push:
branches: [ main ]
paths:
- doc/*
- src/*/*.scala
jobs:
admin:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Tidy repository
uses: propensive/tumult@0.5.8
- name: Autocommit changes
uses: stefanzweifel/git-auto-commit-action@v4
27 changes: 27 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Build
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Java 19
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '19'
- name: Get Wrath
uses: actions/checkout@v3
with:
repository: 'propensive/wrath'
path: 'wrath'
ref: '0.6.1'
- name: Build
run: "wrath/wrath -F -x"
47 changes: 47 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Publish
on:
push:
tags:
- 'pending/*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
submodules: recursive
fetch-depth: 0
- name: Setup Java 11
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: '11'
- name: Fetch tags
run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
- name: Get Version
id: tagName
run: echo ::set-output name=version::${GITHUB_REF#refs/tags/pending/}
- name: 'Abort if tag exists'
id: checkTagged
shell: bash
run: git show-ref --tags --verify --quiet -- "refs/tags/release/${{ steps.tagName.outputs.version }}" && exit 1 || exit 0
- name: Publish
run: echo Publishing
- name: Tag release
uses: actions/github-script@v6
with:
github-token: ${{ secrets.RELEASE_TOKEN }}
script: |
let newRef = context.ref.replace('pending', 'release');
github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: newRef,
sha: context.sha
});
github.rest.git.deleteRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: context.ref.substring(5)
});
19 changes: 19 additions & 0 deletions fury
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# This is a buildfile for Fury or Wrath.
# More information is available at: https://github.com/propensive/wrath/

target metamorphose/test

repo propensive/probably

project metamorphose
module core
compiler scala
sources src/core
include fulminate/core contingency/core

module test
compiler scala
sources src/test
include probably/cli metamorphose/core
main metamorphose.Tests
coverage metamorphose/core
34 changes: 34 additions & 0 deletions src/core/metamorphose.Factoradic.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package metamorphose

import contingency.*

import scala.annotation.*

case class Factoradic(number: BigInt):
def expand: List[Int] =
@tailrec
def recur(current: BigInt, sequence: List[BigInt], result: List[Int]): List[Int] =
sequence match
case Nil =>
result.reverse

case head :: tail =>
val next = (current/head).toInt
recur(current - next*head, tail, next :: result)

if number == 0 then Nil
else recur(number, Factorial.sequence(Factorial.magnitude(number) - 1), Nil)

object Factoradic:
def apply(sequence: List[Int]): Factoradic raises PermutationError =
def recur(sequence: List[Int], bases: List[BigInt], result: BigInt, base: Int): BigInt =
sequence match
case Nil => result
case head :: tail =>
if head >= base
then raise(PermutationError(PermutationError.Reason.BaseRange(head, base)))(())

recur(tail, bases.tail, result + bases.head*head, base - 1)

val length = sequence.length
Factoradic(recur(sequence, Factorial.sequence(length), 0, length))
23 changes: 23 additions & 0 deletions src/core/metamorphose.Factorial.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package metamorphose

import scala.annotation.*

object Factorial:
def apply(n: Int): BigInt =
@tailrec
def recur(i: Int, result: BigInt): BigInt = if i == 0 then result else recur(i - 1, result*i)

recur(n, 1)

def magnitude(n: BigInt): Int =
@tailrec
def recur(i: Int, result: BigInt): Int = if result > n then i else recur(i + 1, result*i)

recur(1, 1)

def sequence(n: Int): List[BigInt] =
@tailrec
def recur(i: Int, result: List[BigInt]): List[BigInt] =
if i == n then result else recur(i + 1, result.head*i :: result)

recur(1, List(1))
78 changes: 78 additions & 0 deletions src/core/metamorphose.Permutation.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package metamorphose

import scala.collection.mutable.BitSet
import scala.annotation.*

import contingency.*

object Permutation:

def bySize(n: Int): LazyList[Permutation] = LazyList.range[BigInt](0, Factorial(n)).map: i =>
Permutation(Factoradic(i))

def apply(sequence: IndexedSeq[Int]): Permutation raises PermutationError =
val array: Array[Int] = new Array(sequence.length)
val seen: BitSet = BitSet()

for index <- sequence.indices do
val element = sequence(index)
array(index) = element - seen.count(_ < element)

if element >= sequence.length || element < 0
then
raise
(PermutationError(PermutationError.Reason.InvalidIndex(element, sequence.length - 1)))(())

if seen.contains(element)
then raise(PermutationError(PermutationError.Reason.DuplicateIndex(element, index)))(())

seen(element) = true

Permutation(Factoradic(array.to(List)))

case class Permutation(factoradic: Factoradic):
lazy val lehmer: List[Int] = factoradic.expand
lazy val expansion: List[Int] = unsafely(apply[Int](List.range(0, lehmer.length)))

def apply(n: Int): Int = expansion(n)

def apply[ElementType](sequence: List[ElementType]): List[ElementType] raises PermutationError =
if sequence.length < lehmer.length then
raise(PermutationError(PermutationError.Reason.TooShort(sequence.length, lehmer.length)))(())

def recur
(lehmer: List[Int],
prefix: List[ElementType],
list: List[ElementType],
current: Int,
result: List[ElementType])
: List[ElementType] =

lehmer match
case head :: tail =>
if current == head
then recur(tail, prefix, list.tail, current, list.head :: result)
else
if current < head
then recur(lehmer, list.head :: prefix, list.tail, current + 1, result)
else recur(lehmer, prefix.tail, prefix.head :: list, current - 1, result)

case Nil =>
result.reverse

val prefix = sequence.length - lehmer.length
sequence.take(prefix) ::: recur(lehmer, Nil, sequence.drop(prefix), 0, Nil)

def inverse: Permutation = if lehmer.isEmpty then this else
val length = lehmer.length
val array: Array[Int] = new Array(lehmer.length)

def recur(index: Int, sequence: List[Int]): Permutation = sequence match
case head :: tail =>
array(head) = index
recur(index + 1, tail)

case Nil =>
unsafely(Permutation(IArray.from(array)))

recur(0, expansion)
28 changes: 28 additions & 0 deletions src/core/metamorphose.PermutationError.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package metamorphose

import fulminate.*

object PermutationError:
enum Reason:
case BaseRange(value: Int, base: Int)
case DuplicateIndex(index: Int, element: Int)
case InvalidIndex(last: Int, max: Int)
case TooShort(length: Int, min: Int)

import Reason.*

given Reason is Communicable =
case BaseRange(value, base) =>
msg"the value $value is too large for its positional base $base"

case DuplicateIndex(element, index) =>
msg"the index $element was duplicated at $index"

case InvalidIndex(index, max) =>
msg"the index $index appears, but every index should be in the range 0-$max"

case TooShort(size, min) =>
msg"the input, of size $size, is too short for the permutation of size $min"

case class PermutationError(reason: PermutationError.Reason)
extends Error(msg"could not construct permutation because $reason")
Loading

0 comments on commit a49ba2e

Please sign in to comment.