Calculadora de números complejos en Kotlin

Calculadora de números complejos en Kotlin
I kt
Siempre me ha gustado C#, quero recordar cuando apareció Linq y las funciones lambda, operador Elvis, inicializadores de propiedades automáticas, la magia asincrónica, etc.. Java añadió a su código novedades, la guerra de «mi lenguaje es mejor» y… de un lenguaje de una empresa que casi nadie conocía, se va haciendo hueco hasta que Google alabando su velocidad de compilación, facilidad y simplicidad, lo nombra lenguaje oficial de Android allá por el 2017. En 2018, me da por investigar Kotlin, porque no seré el primero que ha aprendido un lenguaje que luego no ha pasado de cuartos y según avanzaba con Kotlin, más y más me gustaba, de hecho cada día me asombra y descubro alguna bondad que me aporta.Bueno, ya que he halagado, alabado, agasajado, piropeado y adulado a Kotlin, retomo una calculadora de números complejos en este lenguaje ya que la tengo en este blog en C# y Java y se ha alineado las mates y este lenguaje, así que no hay más remedio (le tocará a la calculadora IP, a matrices, a…).

Lo mejor de esta, su simplicidad en tan poco tiempo, seguro que si le dedicas un poco de tiempo, se puede simplificar más, pero esto lo dejo para vosotros. He aquí el código de una calculadora de números complejos para consola o para lo que queráis con Kotlin.


class Complex (var x: Double = 0.0, var y: Double = 0.0, val isDegrees: Boolean = false){

    var module: Double = 0.toDouble()
    var grades: Double = 0.toDouble()

    init {
        this.module = Math.sqrt(Math.pow(x, 2.0) + Math.pow(x, 2.0))
        this.grades = Math.atan2(y, x)
    }

    operator fun plus(complex: Complex):Complex = plusComplex(complex)
    operator fun minus(complex: Complex):Complex = substractComplex(complex)
    operator fun times(complex: Complex):Complex = multiplyComplex(complex)
    operator fun div(complex: Complex):Complex = divideComplex(complex)

    fun toVectorialString(): String ="(${toNumber(this.x, 2)}, ${toNumber(this.y, 2)})"

    override fun toString(): String ="${toNumber(this.x, 2)} ${if (this.y < 0) "" else  "+"} ${toNumber(this.y, 2)}i"

    fun toPolarString(): String {
        val angle = if (isDegrees) this.grades * 180 / Math.PI else this.grades
        val angleToString =
            "${(if (isDegrees) toNumber(angle, 1) else toNumber(angle, 3))} ${if (isDegrees) 'º' else " radians"}"
        return "${toNumber(this.module, 1)} )  $angleToString"
    }

    fun conjugate(complex: Complex = this): Complex = Complex(complex.x, -complex.y)

    fun opposite(complex: Complex = this): Complex = Complex(-complex.x, -complex.y)

    fun reverse(complex: Complex = this): Complex {
        return Complex(complex.x / denominator(), -complex.y / denominator())
    }

    fun equals(complex: Complex?): Boolean =complex != null && this.javaClass == complex.javaClass && this.x == complex.x && this.y == complex.y

    private fun denominator(complex: Complex=this) = Math.pow(complex.x, 2.0) + Math.pow(complex.y, 2.0)

    private fun plusComplex(complex: Complex): Complex {
        return Complex(this.x + complex.x, this.y + complex.y)
    }

    private fun substractComplex(complex: Complex): Complex = Complex(this.x - complex.x, this.y - complex.y)

    private fun multiplyComplex(complex: Complex): Complex {
        return Complex(
            this.x * complex.x - this.y * complex.y,
            this.x * complex.y + this.y * complex.x
        )
    }

    private fun divideComplex(complex: Complex): Complex = divideComplex(this, complex)

    private fun divideComplex(complex1: Complex, complex2: Complex): Complex {
        val xx = (complex1.x * complex2.x + complex1.y * complex2.y) / (Math.pow(
            complex2.x,
            2.0
        ) + Math.pow(complex2.y, 2.0))
        val yy = (complex1.y * complex2.x - complex1.x * complex2.y) / (Math.pow(
            complex2.x,
            2.0
        ) + Math.pow(complex2.y, 2.0))
        return Complex(xx, yy)
    }

    companion object {

        fun convertToBinomial(module: Double, argument: Double):Complex = convertPolarToBinomial(module, argument)

        fun convertPolarToBinomial(module: Double, argument: Double): Complex = Complex(abs(module) * cos(argument), -abs(module) * sin(argument))

        fun toNumber(number: Double, digits: Int): String {
            val fn = NumberFormat.getNumberInstance()
            fn.maximumFractionDigits = digits
            return fn.format(number)
        }
    }
}


y el resultado


fun main(args:Array){
    val c1 = Complex(3.0,4.0)
    val c2 = Complex(5.0,6.0)

    println(c1.toString())


    println(c2.toString())
    println(c1+c2)
    println(c1-c2)
    println(c1*c2)
    println(c1/c2)
    println(c1.reverse())
    println(c1.conjugate())

}

3 + 4i
5 + 6i
8 + 10i
-2  -2i
-9 + 38i
0,64 + 0,03i
0,12  -0,16i
3  -4i

PD: Kotlin es el lenguaje oficial de Android, pero también permite desarrollar aplicaciones de consola, de escritorio, WEB, etc., es multiplataforma, ya que corre bajo la JVM y puede ser compilado a JavaScript.

Señores de WordPress, ¿Cuando podré publicar lenguaje Kotlin en su editor?

Dead XOR Alive

Dead XOR Alive

En uno de mis blogs, en Arithmos, publiqué un post sobre una historia de carceleros y presos; minutos después de acabar el artículo, dije, ¿por qué no hago una app que lo muestre?, dicho y hecho, me puse y en una tarde desarrollada estaba.

Y ¿por qué digo esto? para demostrar lo fácil que es desarrollar una pequeña aplicación con C# y WPF.

Si leen el artículo, podrán observar que la aplicación solo muestra un tablero de ajedrez, con monedas en cada casilla con una cara o una cruz aleatoriamente, un cuadro mágico que es el elegido por el carcelero y un cuadro del tablero que debe calcular uno de los presos para que el siguiente convicto les salve de la muerte, este último cuadro no es otro que una operación XOR.

El modelo podría ser una clase Chessboard que alberga objetos del tipo CoinBox, las monedas que ya de camino le diremos que color tiene el cuadro donde se apoyan en el tablero.

    public class Chessboard
    {
        public List CoinsBox { get; set; }

        public Chessboard() : this(-1) { }

        public Chessboard(int headsCount)
        {
            CoinsBox = new List();
            FillChessboard(headsCount);
        }

        ///
        /// Rellena el tablero con cuadros y monedas aleaotorias con cara y cruz
        /// 

        /// Número de caras. si es -1, se genera aletoriamente
        void FillChessboard(int headsCount)
        {
            var random = new Random();

            headsCount = headsCount == -1 ? random.Next(64) : headsCount;

            // Llenar Lista con monedas con cruz
            Enumerable.Range(0, 64).ToList()
            .ForEach(n => CoinsBox.Add(new CoinBox(n, false, (n / 8) % 2 == 0 ? n % 2 == 0 : n % 2 != 0)));

            // Cambiar n monedas a cara
            Enumerable.Range(0, headsCount).ToList().ForEach(n => CoinsBox[random.Next(64)].IsHeads = true);

            // Cuadro mágico
            CoinsBox[random.Next(64)].IsMagicBox = true;

            // Cuadro que debe cambiar
            var changed= CoinsBox
                .Where(n => n.IsHeads).Aggregate(MagicBox.Index, (acum, n) => acum ^ n.Index, op => op);
            CoinsBox[changed].IsChangedBox = true;

        }

        public CoinBox MagicBox => CoinsBox.Where(n => n.IsMagicBox).FirstOrDefault();
        public CoinBox ChangedBox => CoinsBox.Where(n => n.IsChangedBox).FirstOrDefault();
        public int ParityChessboard => CoinsBox
                .Where(n => n.IsHeads).Aggregate(0, (acum, n) => acum ^ n.Index, op => op);

        public string ParityChessboardToString => $"La paridad del tablero es {ParityChessboard}";
        public string ResultToString => $"Si cambiamos la moneda {ChangedBox.Index}, la paridad del tablero será {MagicBox.Index} que es el cuadrado mágico!!!";
        public string MagicBoxToString => $"El cuadro mágico seleccionado por el carcelero es el número {MagicBox.Index}";
        public string ChangeBoxToString => $"La moneda que debe cambiar el convicto es la número {ChangedBox.Index}";
        public string ListaXORToString => $"La operación XOR de las monedas que tienen cara {string.Join(", ", CoinsBox.Where(k => k.IsHeads).Select(i => i.Index))} y el cuadro mágico {MagicBox.Index} da como resultado {ChangedBox.Index} o la operación XOR entre el cuadro mágico {MagicBox.Index} y la paridad del tablero {ParityChessboard} es {ChangedBox.Index}";
    }

    public class CoinBox
    {

        public CoinBox(int index, bool isHeads, bool isBlack)
        {
            Index = index;
            IsHeads = isHeads;
            IsBlack = isBlack;
        }

        public int Index { get; set; }

        public bool IsHeads { get; set; }

        public bool IsTails { get { return !IsHeads; } }

        public bool IsChangedBox { get; set; }

        public bool IsMagicBox { get; set; }

        public bool IsBlack { get; set; }
    }

Como algo particular, la asignación del color del cuadro mediante la operación (n / 8) % 2 == 0 ? n % 2 == 0 : n % 2 != 0) con la que con el índice del cuadro le decimos si el cuadro es negro o blanco o las expresiones lambda que calculan la operación XOR sobre una lista de elementos mediante la función lambda Aggregate, la cual le pasamos un parámetro acumulador inicial y posteriormente es usado elemento a elemento con la operación XOR.Para la vista, he creado un control definido por el usuario llamado Box, el cual contiene el Binding cambiando su aspecto según las propiedades del objeto CoinBox asociado a su Datacontext. Como podéis ver a continuación, creo converter para transformar valores booleanos en Visibility, Brush o Bitmap. (Si alguien los quiere los converter, los cuelgo)

<UserControl x:Class="DxorAlive.Box"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:DxorAlive"
             mc:Ignorable="d" 
             d:DesignHeight="43" d:DesignWidth="43" Margin="1">
    <UserControl.Resources>
        <local:BoolToVisibleConverter x:Key="b2tvc" Reverse="False"/>
        <local:ImageConverter x:Key="itc"/>
        <local:BoolToColorConverter x:Key="btcc"/>
    </UserControl.Resources>
    <Grid>

        <Rectangle Width="64" Height="64" Stroke="Gray" StrokeThickness="1" Fill="{Binding IsBlack, Converter={StaticResource btcc}}"/>
        <Rectangle Width="62" Height="62" Stroke="Red" StrokeThickness="2" Visibility="{Binding IsMagicBox, Converter={StaticResource b2tvc}}"/>
        <Rectangle Width="60" Height="60" Stroke="Orange" StrokeThickness="2" Visibility="{Binding IsChangedBox, Converter={StaticResource b2tvc}}"/>
        <Image Source="{Binding IsHeads, Converter={StaticResource itc}}" Height="48"/>
    </Grid>
</UserControl>

Ahora solo nos queda crear o con xml o desde la clase de la vista (runtime) una matriz de objetos cada uno con su datacontext. Yo lo he he desarrollado con código en vez de crear 64 cuadros del tipo Box. Llamamos al método FillChessboard y se genera un nuevo tablero.

        private void FillChessboard(int headsCount)
        {
            grid.Children.Clear();
            // Crear tablero
            var cb = new Chessboard(headsCount);

            //crear cuadros y monedas
            cb.CoinsBox.ForEach(n =>
            {
                var c = new Box();
                c.DataContext = n;
                var row = n.Index / 8;
                var col = n.Index % 8;
                Grid.SetRow(c, row);
                Grid.SetColumn(c, col+1);
                grid.Children.Add(c);
            });

            this.DataContext = cb;

        }

he aquí el resultado. Un saludo a tod@s

deadxoralive1.png
Os dejo el enlace del ejecutable

PD: Y como llevo una temporada enamorado de Kotlin, os paso código en este lenguaje para una app de consola. I ♥ kt

 
import kotlin.random.Random

fun main(args: Array) {
    val c= Chessboard(5)
    println(c)

}


class Chessboard(var headsCount: Int) {
    val coinsBox = mutableListOf()

    init {
        headsCount = if (headsCount == -1) Random.nextInt(64) else headsCount
        fillChessboard()
    }

    private fun fillChessboard() {
        // Rellenar de monedas
        (0..63).toList().forEach {
            coinsBox.add(CoinBox(it, false, if ((it / 8) % 2 == 0) (it % 2 == 0) else (it % 2 != 0)))
        }
        // Caras
        (0..headsCount!!).toList().forEach { coinsBox[Random.nextInt(64)].isHeads = true }
        // cuadro mágico
        val vMB = Random.nextInt(64)
        coinsBox[vMB].isMagicBox = true
        // moneda cambiante
        coinsBox[coinsBox.filter { i-> i.isHeads }.fold(vMB) { acc, opXOR -> acc.xor(opXOR.index) }].isChangedBox=true

    }

    override fun toString(): String {
        return "$magic\n$parityToString\n$changed\n$result\n$listaXOR\n$chessboard"
    }

    val magicBox = coinsBox.filter { n -> n.isMagicBox }.firstOrNull()
    val changedBox = coinsBox.filter { n -> n.isChangedBox }.firstOrNull()
    val parityChessBoard = coinsBox[coinsBox.filter { i-> i.isHeads }.fold(0) { acc, opXOR -> acc.xor(opXOR.index) }]

    val parityToString= "La paridad del tablero es ${parityChessBoard.index}"
    val result = "Si cambiamos la moneda ${changedBox?.index}, la paridad del tablero será ${magicBox?.index} que es el cuadrado mágico"
    val magic = "El cuadro seleccionado por el carcelero es el número ${magicBox?.index}"
    val changed = "La moneda que debe cambiar el convicto es la número ${changedBox?.index}"
    val listaXOR = "La operación XOR de las monedas que tienen cara ${coinsBox.filter { k-> k.isHeads }.joinToString(", "){it.index.toString()}} " +
            "y el cuadro mágico ${magicBox?.index} da como resultado ${changedBox?.index} " +
            "\no la operación XOR entre el cuadro mágico ${magicBox?.index} y la paridad del tablero ${parityChessBoard.index} es ${changedBox?.index}"
    val chessboard = coinsBox.joinToString("") {k-> "\t${k.index} ${if (k.isHeads) "H" else "T"} ${if ((k.index+1) % 8==0) "\n" else ""}"  }

}


data class CoinBox(
    val index: Int,
    var isHeads: Boolean,
    var isBlack: Boolean,
    var isChangedBox: Boolean = false,
    var isMagicBox: Boolean = false