Exemplo n.º 1
0
function Fraction_QuantizeValue($val, $idx, $frac_prec, &$first_denom, $style_arr, $rnd, $oc)
{
    $ret = Fraction::toFractWithPrecision($val, $frac_prec);
    $nofrac1 = FALSE;
    $redux = FALSE;
    $prime_max = 0;
    if (DebugReporting::$dbg >= 4) {
        printf("fraction = %s/%s (@ prec=%s)\n", $ret->num, $ret->denom, $frac_prec);
    }
    $pos_l = 0;
    $pos_h = $oc;
    // span left hand AND right hand item!
    foreach ($style_arr as $st) {
        switch ($st) {
            default:
                if (!strncmp($st, "prime_max=", 10)) {
                    $prime_max = (int) substr($st, 10);
                } else {
                    if (!strncmp($st, "step=", 5)) {
                        // calculus values may only be values, rounded to a given factor.
                        $factor = (double) substr($st, 5);
                        if (DebugReporting::$dbg >= 2) {
                            printf("<p>fraction step: factor = %s, ret= %s, idx = %d, zero_ok = %d, pos_l = %d, pos_h = %d\n", $factor, $val, $idx, $zero_ok, $pos_l, $pos_h);
                        }
                        if ($idx >= $pos_l && $idx <= $pos_h) {
                            $val = (int) ($val / $factor) * $factor;
                            if (DebugReporting::$dbg >= 2) {
                                printf("<p>fraction step: corrected ret: %s, factor = %s, idx = %d, zero_ok = %d\n", $val, $factor, $idx, $zero_ok);
                            }
                            $ret = Fraction::toFractWithPrecision($val, $frac_prec);
                        }
                    } else {
                        if ($st[0] == 'T') {
                            // value may only be one of a specified set
                            //
                            // originally only intended for [tables of] multiplication, but now usable anywhere.
                            if ($idx >= $pos_l && $idx <= $pos_h) {
                                // allow only particular tables of multiplication: set following the T!
                                // since the multiplication tables may go BEYOND 10 while we only allow multipliers up to 10,
                                // we cannot do this by 'filtering' the currently generated material.
                                // Instead, we just pick the table of choice from the list and apply that to lvalue[0].
                                //
                                // Of course, the answer must be recalculated too!
                                $multipliers = explode(',', substr($st, 1));
                                $diff = 4000000000.0;
                                $val = $ret->Val();
                                foreach ($multipliers as $m) {
                                    $delta = abs($val - (int) $m);
                                    if ($delta < $diff) {
                                        $val = (int) $m;
                                        $ret->num = $val * $ret->denom;
                                        $diff = $delta;
                                    }
                                }
                                if (DebugReporting::$dbg >= 2) {
                                    printf("<p>T table quatization: delta: %s, idx = %d, val: %s\n", $diff, $idx, $val);
                                }
                            }
                        } else {
                            if ($st[0] == 'p' && FALSE !== strpos("3456789", substr($st, 1)) && strlen($st) == 2) {
                                // next filters apply to Nth (and subsequent) lvalues only!
                                $pos_l = (int) substr($st, 1) - 1;
                                $pos_h = $oc - 1;
                            }
                        }
                    }
                }
                break;
            case "p1":
                // next filters apply to first lvalue only!
                $pos_l = 0;
                $pos_h = 0;
                break;
            case "p2":
                // next filters apply to second (and subsequent) lvalues only!
                $pos_l = 1;
                $pos_h = $oc - 1;
                break;
            case "pO":
                // next filters apply to answer (right hand) lvalue only!
                $pos_l = $oc;
                $pos_h = $oc;
                break;
            case "px":
                // next filters apply to ANY lvalues!
                $pos_l = 0;
                $pos_h = $oc - 1;
                break;
            case "nofrac1":
                // we do not accept a divisor of '1'!
                if ($idx >= $pos_l && $idx <= $pos_h) {
                    $nofrac1 = TRUE;
                }
                break;
            case "redux":
                // the produced fraction MUST be reducible
                if ($idx >= $pos_l && $idx <= $pos_h) {
                    $redux = TRUE;
                }
                break;
            case "1denom":
                // only permit one (common) denominator: take the denom of lvalue[0] and apply to all
                if (!$first_denom || $idx == 0) {
                    $first_denom = $ret->denom;
                } else {
                    if ($ret->denom != $first_denom) {
                        // ensure the /value/ of the fraction, which gets its denominator patched, stays close to what it was:
                        $rescale = $first_denom / $ret->denom;
                        $ret->num = (int) ($rescale * $ret->num + 0.5);
                        $ret->denom = $first_denom;
                    }
                }
                break;
            case "1denom_M":
                // only permit one (common) denominator: take the denom of lvalue[0] and apply to all
                //
                // same as '1denom' apart from the fact that it also accepts 'non-reduced' fractions with the
                // same denominator, i.e. if denominator has been set to be '8', the fraction '1/2' is okay
                // too as it can be expressed in 8ths: '4/8'.
                //
                // It also picks the denominator at random, while '1denom' just picks the denominator of the
                // first term.
                if (!$first_denom || $idx == 0) {
                    // pick a 'common denominator' at random and apply that one to each term as it comes along.
                    $m_range = (int) (1.0 / ($frac_prec * $ret->denom));
                    $m = (int) ($d_range * $rnd->frand()) + 1;
                    $d = $ret->denom * $m;
                    if ($prime_max > 0 && !CheckPrimeMax($d, $prime_max, $frac_prec)) {
                        $d = $ret->denom;
                    }
                    $first_denom = $d;
                }
                $factor = $first_denom / $ret->denom;
                // is our fraction expressible as a $first_denom fraction? if not: tweak!
                if (!FltEquals((int) $factor, $factor, $frac_prec)) {
                    // ensure the /value/ of the fraction, which gets its denominator patched, stays close to what it was:
                    $ret->num = (int) ($factor * $ret->num + 0.5);
                    $ret->denom = $first_denom;
                }
                break;
        }
    }
    if ($prime_max > 0 && !CheckPrimeMax($ret->denom, $prime_max, $frac_prec)) {
        if (DebugReporting::$dbg >= 1) {
            printf("<p>boom at checkprimemax (%s/%s @ %s)\n", $ret->num, $ret->denom, $prime_max);
        }
        throw new Exception(sprintf("value not acceptable: fraction denominator %s is contains primes beyond the maximum allowed %s", $ret->denom, $prime_max));
    }
    if ($redux) {
        // ensure the fraction is reducible!
        $gcd = CalcGCD($ret->num, $ret->denom);
        if ($gcd <= 1) {
            $m = 1;
            if (!$first_denom || $idx == 0) {
                $m_range = (int) (1.0 / ($frac_prec * $ret->denom)) - 1;
                $m = 2 + (int) ($m_range * $rnd->frand());
                $first_denom = $m * $ret->denom;
            }
            if ($m <= 1) {
                if (DebugReporting::$dbg >= 1) {
                    printf("<p>boom at GCD (%s/%s @ %s / first=%s / idx = %s)\n", $ret->num, $ret->denom, $m, $first_denom, $idx);
                }
                throw new Exception(sprintf("value not acceptable: fraction %s/%s is not expandible nor reducible", $ret->num, $ret->denom));
            }
        }
    }
    return $ret->Val();
}